Skip to content

Commit 8e4dca1

Browse files
committed
add example Lustre plugin for adding lustre stripe info to the DBs
1 parent 6aa74e3 commit 8e4dca1

File tree

2 files changed

+365
-0
lines changed

2 files changed

+365
-0
lines changed

contrib/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,11 @@ foreach(file ${USEFUL})
132132
endforeach()
133133

134134
add_subdirectory(CI)
135+
136+
# See if lustre-devel is installed by checking if the lustreapi.h header is in a standard include path.
137+
find_path(LUSTRE_PLUGIN "lustre/lustreapi.h")
138+
# If so, build the lustre example plugin:
139+
if (LUSTRE_PLUGIN)
140+
add_library(lustre_plugin SHARED lustre_plugin.c)
141+
target_link_libraries(lustre_plugin lustreapi)
142+
endif()

contrib/lustre_plugin.c

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
/*
2+
This file is part of GUFI, which is part of MarFS, which is released
3+
under the BSD license.
4+
5+
6+
Copyright (c) 2017, Los Alamos National Security (LANS), LLC
7+
All rights reserved.
8+
9+
Redistribution and use in source and binary forms, with or without modification,
10+
are permitted provided that the following conditions are met:
11+
12+
1. Redistributions of source code must retain the above copyright notice, this
13+
list of conditions and the following disclaimer.
14+
15+
2. Redistributions in binary form must reproduce the above copyright notice,
16+
this list of conditions and the following disclaimer in the documentation and/or
17+
other materials provided with the distribution.
18+
19+
3. Neither the name of the copyright holder nor the names of its contributors
20+
may be used to endorse or promote products derived from this software without
21+
specific prior written permission.
22+
23+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
24+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26+
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
31+
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33+
34+
35+
From Los Alamos National Security, LLC:
36+
LA-CC-15-039
37+
38+
Copyright (c) 2017, Los Alamos National Security, LLC All rights reserved.
39+
Copyright 2017. Los Alamos National Security, LLC. This software was produced
40+
under U.S. Government contract DE-AC52-06NA25396 for Los Alamos National
41+
Laboratory (LANL), which is operated by Los Alamos National Security, LLC for
42+
the U.S. Department of Energy. The U.S. Government has rights to use,
43+
reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR LOS
44+
ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
45+
ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is
46+
modified to produce derivative works, such modified software should be
47+
clearly marked, so as not to confuse it with the version available from
48+
LANL.
49+
50+
THIS SOFTWARE IS PROVIDED BY LOS ALAMOS NATIONAL SECURITY, LLC AND CONTRIBUTORS
51+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
52+
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53+
ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL SECURITY, LLC OR
54+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
55+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
56+
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
59+
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
60+
OF SUCH DAMAGE.
61+
*/
62+
63+
64+
65+
/*
66+
* An example Lustre plugin for GUFI.
67+
*
68+
* If the lustre and lustre-devel RPMs are installed on the machine that is used to build GUFI, then this plugin
69+
* should be built automatically. If you want to build it manually (for example, because the lustre source is available
70+
* but not installed into a standard path), you can do so like this:
71+
*
72+
* # set based on the lustre source location on your machine:
73+
* $ export LUSTRE_INCLUDE_DIR=/home/$USER/lustre-release/lustre/include
74+
* $ export LUSTRE_LIBRARY_DIR=/home/$USER/lustre-release/lustre/utils/.libs
75+
*
76+
* $ cd contrib
77+
*
78+
* $ gcc -g -c -fPIC -I../build/deps/sqlite3/include -I../include -I$LUSTRE_INCLUDE_DIR -I$LUSTRE_INCLUDE_DIR/uapi lustre_plugin.c
79+
*
80+
* $ gcc -shared -o liblustre_plugin.so lustre_plugin.o -L$LUSTRE_LIBRARY_DIR -llustreapi
81+
*
82+
* Note that this includes the sqlite3 header from the GUFI sources, but this should not
83+
* statically link sqlite3. Instead, it should dynamically link to the sqlite3 symbols in
84+
* the main GUFI binary when this code is dlopen()ed.
85+
*
86+
* Run like this:
87+
*
88+
* # if the lustreapi library is not installed in a standard location:
89+
* $ export LD_LIBRARY_PATH=$LUSTRE_LIBRARY_DIR
90+
*
91+
* $ ./src/gufi_dir2index -U ../contrib/liblustre_plugin.so -n1 /mnt/lustre /tmp/gufi_index/
92+
*/
93+
94+
#include <errno.h>
95+
#include <inttypes.h>
96+
#include <stdio.h>
97+
#include <string.h>
98+
99+
#include "plugin.h"
100+
#include "sqlite3.h"
101+
#include "lustre/lustreapi.h"
102+
103+
static char *my_basename(char *path) {
104+
char *base = path;
105+
char *p = path;
106+
int next_component = 0;
107+
108+
while (*p) {
109+
if (next_component) {
110+
base = p;
111+
next_component = 0;
112+
}
113+
114+
if (*p == '/') {
115+
next_component = 1;
116+
}
117+
118+
p++;
119+
}
120+
121+
return base;
122+
}
123+
124+
/*
125+
* This struct tracks the number of components seen on each OST. For each file
126+
* processed, and for each component of that file, add one to the count for
127+
* that particular OST.
128+
*/
129+
struct stripe_tracker {
130+
/*
131+
* num_components[i] stores the number of components that have been seen
132+
* on the OST with index i.
133+
*/
134+
uint64_t *stripe_count;
135+
/*
136+
* Stores the size of the num_components table. This needs to grow when we see a
137+
* new OST number that is higher than the maximum index in the table so far.
138+
*/
139+
uint32_t array_size;
140+
/*
141+
* Tracks the highest OST index seen. This will likely be smaller than the
142+
* array size, so separately tracking it means we don't have to loop through the
143+
* useless high indexes in the stripe_count array when saving the stripe info
144+
* to the database.
145+
*/
146+
uint32_t max_ost_idx;
147+
};
148+
149+
/*
150+
* Allocate and Initialize a new stripe_tracker.
151+
*/
152+
static struct stripe_tracker *new_stripe_tracker(void) {
153+
uint32_t initial_size = 64;
154+
155+
struct stripe_tracker *new = malloc(sizeof *new);
156+
if (!new) {
157+
return NULL;
158+
}
159+
160+
new->stripe_count = calloc(initial_size, sizeof(*new->stripe_count));
161+
if (!new->stripe_count) {
162+
free(new);
163+
return NULL;
164+
}
165+
166+
new->array_size = initial_size;
167+
168+
new->max_ost_idx = 0;
169+
170+
return new;
171+
}
172+
173+
/*
174+
* Clean up a stripe_tracker, freeing its allocations.
175+
*/
176+
static void destroy_stripe_tracker(struct stripe_tracker *p) {
177+
if (p) {
178+
free(p->stripe_count);
179+
}
180+
181+
free(p);
182+
}
183+
184+
/*
185+
* If necessary, grow the stripe_count array in `s` to be large enough to
186+
* accomodate `ost_index`.
187+
*
188+
* Returns 0 if growing succeeded, or 1 if it failed.
189+
*/
190+
static int grow_stripe_tracker(struct stripe_tracker *s, uint32_t ost_index) {
191+
if (ost_index > s->max_ost_idx) {
192+
s->max_ost_idx = ost_index;
193+
}
194+
if (ost_index < s->array_size) {
195+
/* Nothing needs to be done: yay! */
196+
return 0;
197+
}
198+
199+
uint32_t new_size = s->array_size;
200+
201+
while (ost_index >= new_size) {
202+
if (new_size >= UINT32_MAX / 2) {
203+
/* In case we somehow get a filesystem with an insane number of OSTs,
204+
* don't let that overflow and ruin our array: */
205+
return 1;
206+
}
207+
new_size *= 2;
208+
}
209+
210+
uint64_t *new_array = realloc(s->stripe_count, new_size);
211+
if (!new_array) {
212+
return 1;
213+
}
214+
215+
/* The new space is not initialized, so do that now: */
216+
memset(new_array + s->array_size, 0, new_size - s->array_size);
217+
218+
s->stripe_count = new_array;
219+
s->array_size = new_size;
220+
221+
return 0;
222+
}
223+
224+
/*
225+
* Given the `stripe_array` which contains `stripe_count` stripes, increment
226+
* the count for each OST that the stripe lives on.
227+
*/
228+
static void track_file_stripes(struct stripe_tracker *s,
229+
struct lov_user_ost_data_v1 *stripe_array,
230+
uint16_t stripe_count) {
231+
for (int i = 0; i < stripe_count; i++) {
232+
uint32_t ost_index = stripe_array[i].l_ost_idx;
233+
if (grow_stripe_tracker(s, ost_index)) {
234+
/* Just give up if we couldn't grow the stripe array large enough :( */
235+
fprintf(stderr, "lustre plugin: could not allocate memory, information may be incomplete");
236+
return;
237+
}
238+
239+
s->stripe_count[ost_index] += 1;
240+
}
241+
}
242+
243+
/*
244+
* Set up initial state for tracking Lustre stripe info.
245+
*/
246+
void *db_init(sqlite3 *db) {
247+
struct stripe_tracker *state = new_stripe_tracker();
248+
if (!state) {
249+
fprintf(stderr, "lustre plugin: could not allocate memory to track stripe info");
250+
return NULL;
251+
}
252+
253+
char *text = "CREATE TABLE lustre_summary (ost_index INTEGER PRIMARY KEY, num_files INTEGER);";
254+
char *error;
255+
256+
int res = sqlite3_exec(db, text, NULL, NULL, &error);
257+
if (res != SQLITE_OK) {
258+
fprintf(stderr, "lustre plugin: db_init(): error executing statement: %d %s\n", res, error);
259+
}
260+
261+
sqlite3_free(error);
262+
263+
return state;
264+
}
265+
266+
/*
267+
* Save stripe tracking info to the database and clean up state.
268+
*/
269+
void db_exit(sqlite3 *db, void *user_data) {
270+
struct stripe_tracker *state = (struct stripe_tracker *) user_data;
271+
272+
for (uint32_t i = 0; i <= state->max_ost_idx; i++) {
273+
char *text = sqlite3_mprintf("INSERT INTO %s VALUES(%" PRIu32 ", %" PRIu64 ");",
274+
"lustre_summary", i, state->stripe_count[i]);
275+
char *error;
276+
277+
int res = sqlite3_exec(db, text, NULL, NULL, &error);
278+
if (res != SQLITE_OK) {
279+
fprintf(stderr, "lustre plugin: db_exit(): error executing statement: %d %s\n",
280+
res, error);
281+
}
282+
283+
sqlite3_free(text);
284+
sqlite3_free(error);
285+
}
286+
287+
destroy_stripe_tracker(state);
288+
};
289+
290+
/*
291+
* This method of determining the maximum possible size of a `lov_user_md` was suggested by
292+
* man 3 llapi_file_get_stripe
293+
*/
294+
static const size_t v1_size = sizeof(struct lov_user_md_v1) + LOV_MAX_STRIPE_COUNT * sizeof(struct lov_user_ost_data_v1);
295+
static const size_t v3_size = sizeof(struct lov_user_md_v3) + LOV_MAX_STRIPE_COUNT * sizeof(struct lov_user_ost_data_v1);
296+
static const size_t lum_size = v1_size > v3_size ? v1_size : v3_size;
297+
298+
static void *alloc_lum() {
299+
return calloc(1, lum_size);
300+
}
301+
302+
void process_file(char *path, sqlite3 *db, void *user_data) {
303+
sqlite3_stmt *statement;
304+
305+
struct lov_user_md *layout_info = alloc_lum();
306+
307+
int res = llapi_file_get_stripe(path, layout_info);
308+
309+
if (res) {
310+
fprintf(stderr, "lustre plugin: error getting stripe info for %s: %s\n",
311+
path, strerror(errno));
312+
free(layout_info);
313+
return;
314+
}
315+
316+
char *text = sqlite3_mprintf("UPDATE entries SET ossint4 = %d where name = '%q';",
317+
layout_info->lmm_stripe_size, my_basename(path));
318+
char *error;
319+
320+
res = sqlite3_exec(db, text, NULL, NULL, &error);
321+
if (res != SQLITE_OK) {
322+
fprintf(stderr, "lustre plugin: process_file(): error executing statement: %d %s\n",
323+
res, error);
324+
goto out;
325+
}
326+
327+
struct lov_user_ost_data_v1 *stripe_array;
328+
uint16_t stripe_count;
329+
330+
if (layout_info->lmm_magic == LOV_USER_MAGIC_V1) {
331+
struct lov_user_md_v1 *v1= (struct lov_user_md_v1 *) layout_info;
332+
stripe_array = v1->lmm_objects;
333+
stripe_count = v1->lmm_stripe_count;
334+
} else if (layout_info->lmm_magic == LOV_USER_MAGIC_V3) {
335+
struct lov_user_md_v3 *v3= (struct lov_user_md_v3 *) layout_info;
336+
stripe_array = v3->lmm_objects;
337+
stripe_count = v3->lmm_stripe_count;
338+
} else {
339+
fprintf(stderr, "lustre plugin: unknown layout format on file %s: %d\n", path, layout_info->lmm_magic);
340+
goto out;
341+
}
342+
343+
struct stripe_tracker *tracker = (struct stripe_tracker *) user_data;
344+
track_file_stripes(tracker, stripe_array, stripe_count);
345+
346+
out:
347+
free(layout_info);
348+
sqlite3_free(error);
349+
sqlite3_free(text);
350+
}
351+
352+
struct plugin_operations gufi_plugin_operations = {
353+
.db_init = db_init,
354+
.process_dir = NULL,
355+
.process_file = process_file,
356+
.db_exit = db_exit,
357+
};

0 commit comments

Comments
 (0)