Whamcloud - gitweb
- make HEAD from b_post_cmd3
[fs/lustre-release.git] / lustre / utils / loadgen.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *   Copyright (C) 2006 Cluster File Systems, Inc.
5  *   Author: Nathan Rutman <nathan@clusterfs.com>
6  *
7  *   This file is part of Lustre, http://www.lustre.org.
8  *
9  *   Lustre is free software; you can redistribute it and/or
10  *   modify it under the terms of version 2 of the GNU General Public
11  *   License as published by the Free Software Foundation.
12  *
13  *   Lustre is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with Lustre; if not, write to the Free Software
20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  * loadgen.c
23  * See how many local OSCs we can start whaling on a OST
24  * We're doing direct ioctls instead of going though a system() call to lctl
25  * to avoid the bash overhead.
26  * Adds an osc / echo client pair in each thread and starts echo transactions.
27  *
28  */
29
30 #include <pthread.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <signal.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <string.h>
40 #include <sys/wait.h>
41 #include <time.h>
42 #include <sys/time.h>
43
44 #include <lnet/lnetctl.h>
45 #include "parser.h"
46 #include "obdctl.h"
47
48 static char cmdname[512];
49 static char target[64] = "";
50 char nid[64] = "";
51 static int live_threads = 0;
52 static int sig_received = 0;
53 static int o_verbose = 4; /* 0-5 */
54 static int my_oss = 0;
55 static int my_ecs = 0;
56
57 static int jt_quit(int argc, char **argv) {
58         Parser_quit(argc, argv);
59         return 0;
60 }
61
62 static int loadgen_usage(int argc, char **argv)
63 {
64         if (argc == 1) {
65                 fprintf(stderr, 
66         "This is a test program used to simulate large numbers of\n"
67         "clients.  The echo obds are used, so the obdecho module must\n"
68         "be loaded.\n"
69         "Typical usage would be:\n"
70         "  loadgen> dev lustre-OST0000       set the target device\n"
71         "  loadgen> start 20                 start 20 echo clients\n"
72         "  loadgen> wr 10 5                  have 10 clients do the brw_write\n"
73         "                                      test 5 times each\n"
74                         );
75         }
76         return (Parser_help(argc, argv));
77 }
78
79 static int loadgen_verbose(int argc, char **argv);
80 static int loadgen_target(int argc, char **argv);
81 static int loadgen_start_echosrv(int argc, char **argv);
82 static int loadgen_start_clients(int argc, char **argv);
83 static int loadgen_wait(int argc, char **argv);
84 static int loadgen_write(int argc, char **argv);
85
86 command_t cmdlist[] = {
87         {"device", loadgen_target, 0,
88          "set target ost name (e.g. lustre-OST0000)\n"
89          "usage: device <name> [<nid>]"},
90         {"dl", jt_obd_list, 0, "show all devices\n"
91          "usage: dl"},
92         {"echosrv", loadgen_start_echosrv, 0, "start an echo server\n"},
93         {"start", loadgen_start_clients, 0, "set up echo clients\n"
94          "usage: start_clients <num>"},
95         {"verbose", loadgen_verbose, 0, "set verbosity level 0-5\n"
96          "usage: verbose <level>"},
97         {"wait", loadgen_wait, 0,
98          "wait for all threads to finish\n"},
99         {"write", loadgen_write, 0,
100          "start a test_brw write test on X clients for Y iterations\n"
101          "usage: write <num_clients> <num_iter> [<delay>]"},
102
103         /* User interface commands */
104         {"help", loadgen_usage, 0, "help"},
105         {"exit", jt_quit, 0, "quit"},
106         {"quit", jt_quit, 0, "quit"},
107         { 0, 0, 0, NULL }
108 };
109
110
111 /* Command flags */
112 #define C_STOP           0x0001
113 #define C_CREATE_EVERY   0x0002  /* destroy and recreate every time */
114 #define C_READ           0x0004
115 #define C_WRITE          0x0008
116
117 struct command_t {
118         int           c_flags;
119         int           c_rpt;
120         int           c_delay;
121 };
122
123 struct kid_t {
124         struct command_t k_cmd;
125         struct kid_t    *k_next;
126         pthread_t        k_pthread;
127         __u64            k_objid;
128         int              k_id;
129         int              k_dev;
130 };
131
132 static struct kid_t *kid_list = NULL;
133
134 static struct kid_t *push_kid(int tnum)
135 {
136         struct kid_t *kid;
137         kid = (struct kid_t *)calloc(1, sizeof(struct kid_t));
138         if (kid == NULL) {
139                 fprintf(stderr, "malloc failure\n");
140                 return NULL;
141         }
142         kid->k_pthread = pthread_self();
143         kid->k_next = kid_list;
144         kid->k_id = tnum;
145         kid_list = kid;
146         live_threads++;
147         return kid;
148 }
149
150 int trigger_count = 0;
151 int waiting_count = 0;
152 int timer_on = 0;
153 int all_done = 1;
154 struct timeval trigger_start;
155 struct command_t trigger_cmd;
156 pthread_mutex_t m_trigger = PTHREAD_MUTEX_INITIALIZER;
157 pthread_cond_t cv_trigger = PTHREAD_COND_INITIALIZER;
158
159 unsigned long long write_bytes;
160 pthread_mutex_t m_count = PTHREAD_MUTEX_INITIALIZER;
161
162 static void trigger(int command, int threads, int repeat, int delay)
163 {
164
165         pthread_mutex_lock(&m_trigger);
166         trigger_cmd.c_flags = command;
167         trigger_cmd.c_rpt = repeat;
168         trigger_cmd.c_delay = delay;
169         trigger_count = threads;
170         if (o_verbose > 4)
171                 printf("trigger %d cmd c=%d f=%x\n", trigger_count,
172                        trigger_cmd.c_rpt, trigger_cmd.c_flags);
173         gettimeofday(&trigger_start, NULL);
174         timer_on = 1;
175         pthread_mutex_lock(&m_count);
176         write_bytes = 0;
177         pthread_mutex_unlock(&m_count);
178
179         pthread_cond_broadcast(&cv_trigger);
180         pthread_mutex_unlock(&m_trigger);
181 }
182
183 static __inline__ void stop_all(int unused)
184 {
185         sig_received++;
186 }
187
188 static void kill_kids(void)
189 {
190         struct kid_t *tmp = kid_list;
191
192         stop_all(SIGINT);
193         trigger(C_STOP, 0, 0, 0);
194         while(tmp) {
195                 pthread_kill(tmp->k_pthread, SIGTERM);
196                 tmp = tmp->k_next;
197         }
198 }
199
200 static void sig_master(int unused)
201 {
202         stop_all(SIGINT);
203         //jt_quit(0, NULL);
204 }
205
206 static int wait_for_threads()
207 {
208         struct kid_t *tmp = kid_list;
209         int rc = 0, status;
210         void *statusp;
211
212         printf("waiting for %d children\n", live_threads);
213
214         while(tmp) {
215                 rc = pthread_join(tmp->k_pthread, &statusp);
216                 status = (long)statusp;
217                 if (o_verbose > 2)
218                         printf("%d: joined, rc = %d, status = %d\n",
219                                tmp->k_id, rc, status);
220                 kid_list = tmp->k_next;
221                 free(tmp);
222                 tmp = kid_list;
223                 live_threads--;
224         }
225
226         if (o_verbose > 0)
227                 printf("%s done, rc = %d\n", cmdname, rc);
228         return rc;
229 }
230
231 static int write_proc(char *proc_path, char *value)
232 {
233         int fd, rc;
234
235         fd = open(proc_path, O_WRONLY);
236         if (fd == -1) {
237                 fprintf(stderr, "open('%s') failed: %s\n",
238                         proc_path, strerror(errno));
239                 rc = errno;
240         } else {
241                 rc = write(fd, value, strlen(value));
242                 if (rc < 0) {
243                         fprintf(stderr, "write('%s') failed: %s\n",
244                                 proc_path, strerror(errno));
245                 }
246                 close(fd);
247         }
248         return rc;
249 }
250
251 static int read_proc(char *proc_path,  unsigned long long *value)
252 {
253         int fd, rc;
254         char buf[50];
255
256         fd = open(proc_path, O_RDONLY);
257         if (fd == -1) {
258                 fprintf(stderr, "open('%s') failed: %s\n",
259                         proc_path, strerror(errno));
260                 return (errno);
261         }
262
263         rc = read(fd, buf, sizeof(buf));
264         close(fd);
265         if (errno == EOPNOTSUPP) {
266                 /* probably an echo server */
267                 return rc;
268         }
269         if (rc <= 0) {
270                 fprintf(stderr, "read('%s') failed: %s (%d)\n",
271                         proc_path, strerror(errno), errno);
272                 return rc;
273         }
274         *value = strtoull(buf, NULL, 10);
275         return 0;
276 }
277
278 static int grant_estimate(int thread)
279 {
280         unsigned long long avail, grant;
281         char proc_path[50];
282         int rc;
283         static int ran = 0;
284
285         /* I don't really care about protecting this with a mutex */
286         if (ran)
287                 return 0;
288
289         if (o_verbose < 2)
290                 return 0;
291
292         /* Divide /proc/fs/lustre/osc/o_0001/kbytesavail
293            by /proc/fs/lustre/osc/o_0001/cur_grant_bytes to find max clients */
294         sprintf(proc_path, "/proc/fs/lustre/osc/o%.5d/kbytesavail", thread);
295         rc = read_proc(proc_path, &avail);
296         if (rc)
297                 return rc;
298         sprintf(proc_path, "/proc/fs/lustre/osc/o%.5d/cur_grant_bytes", thread);
299         rc = read_proc(proc_path, &grant);
300         if (rc)
301                 return rc;
302         if (grant == 0) {
303                 return -EINVAL;
304         }
305         printf("Estimate %llu clients before we run out of grant space "
306                "(%lluK / %llu)\n", (avail << 10)  / grant, avail, grant);
307         ran++;
308         return 0;
309 }
310
311 /* We hold a thread mutex around create/cleanup because cur_dev is not
312    shared-memory safe */
313 pthread_mutex_t m_config = PTHREAD_MUTEX_INITIALIZER;
314
315 static int cleanup(char *obdname, int quiet)
316 {
317         char *args[3];
318         int rc;
319
320         pthread_mutex_lock(&m_config);
321
322         args[0] = cmdname;
323         args[1] = obdname;
324         rc = jt_lcfg_device(2, args);
325         if (rc && !quiet)
326                 fprintf(stderr, "%s: can't configure '%s' (%d)\n",
327                         cmdname, obdname, rc);
328         args[1] = "force";
329         rc = jt_obd_cleanup(2, args);
330         if (rc && !quiet)
331                 fprintf(stderr, "%s: can't cleanup '%s' (%d)\n",
332                         cmdname, obdname, rc);
333         rc = jt_obd_detach(1, args);
334         if (rc && !quiet)
335                 fprintf(stderr, "%s: can't detach '%s' (%d)\n",
336                         cmdname, obdname, rc);
337
338         pthread_mutex_unlock(&m_config);
339         return rc;
340 }
341
342 static int echocli_setup(char *oname, char *ename, int *dev)
343 {
344         char *args[5];
345         char proc_path[50];
346         int rc;
347
348         pthread_mutex_lock(&m_config);
349
350         args[0] = cmdname;
351
352         /* OSC */
353         /* attach "osc" oscname oscuuid */
354         args[1] = "osc";
355         args[2] = args[3] = oname;
356         rc = jt_lcfg_attach(4, args);
357         if (rc) {
358                 fprintf(stderr, "%s: can't attach osc '%s' (%d)\n",
359                         cmdname, oname, rc);
360                 /* Assume we want e.g. an old one cleaned anyhow. */
361                 goto clean;
362         }
363         /* setup ostname "OSS_UUID" */
364         args[1] = target;
365         args[2] = "OSS_UUID";
366         rc = jt_lcfg_setup(3, args);
367         if (rc) {
368                 fprintf(stderr, "%s: can't setup osc '%s' (%d)\n",
369                         cmdname, oname, rc);
370                 goto clean;
371         }
372
373         /* Large grants cause ENOSPC to be reported, even though
374            there's space left.  We can reduce the grant size by
375            minimizing these */
376         sprintf(proc_path, "/proc/fs/lustre/osc/%s/max_dirty_mb", oname);
377         rc = write_proc(proc_path, "1");
378         sprintf(proc_path, "/proc/fs/lustre/osc/%s/max_rpcs_in_flight", oname);
379         rc = write_proc(proc_path, "1");
380
381         /* ECHO CLI */
382         /* attach "echo_client" echoname echouuid */
383         args[1] = "echo_client";
384         args[2] = args[3] = ename;
385         rc = jt_lcfg_attach(4, args);
386         if (rc) {
387                 fprintf(stderr, "%s: can't attach '%s' (%d)\n",
388                         cmdname, ename, rc);
389                 if (rc == ENODEV)
390                         fprintf(stderr, "%s: is the obdecho module loaded?\n",
391                                 cmdname);
392                 goto clean;
393         }
394         /* setup oscname */
395         args[1] = oname;
396         rc = jt_lcfg_setup(2, args);
397         if (rc) {
398                 fprintf(stderr, "%s: can't setup '%s' (%d)\n",
399                         cmdname, ename, rc);
400                 goto clean;
401         }
402
403         args[1] = ename;
404         rc = jt_obd_device(2, args);
405         if (rc) {
406                 fprintf(stderr, "%s: can't set device '%s' (%d)\n",
407                         cmdname, ename, rc);
408                 goto clean;
409         }
410
411         if (!rc)
412                 *dev = jt_obd_get_device();
413         pthread_mutex_unlock(&m_config);
414         return rc;
415
416 clean:
417         pthread_mutex_unlock(&m_config);
418         cleanup(ename, 1);
419         cleanup(oname, 1);
420         return rc;
421 }
422
423 /* We can't use the libptlctl library fns because they are not shared-memory
424    safe with respect to the ioctl device (cur_dev) */
425 static int obj_ioctl(int cmd, struct obd_ioctl_data *data, int unpack)
426 {
427         char *buf = NULL;
428         int rc;
429
430         //IOC_PACK(cmdname, data);
431         if (obd_ioctl_pack(data, &buf, sizeof(*data))) {
432                 fprintf(stderr, "dev %d invalid ioctl\n", data->ioc_dev);
433                 rc = EINVAL;
434                 goto out;
435         }
436
437         rc = l_ioctl(OBD_DEV_ID, cmd, buf);
438
439         if (unpack) {
440                 //IOC_UNPACK(argv[0], data);
441                 if (obd_ioctl_unpack(data, buf, sizeof(*data))) {
442                         fprintf(stderr, "dev %d invalid reply\n", data->ioc_dev);
443                         rc = EINVAL;
444                         goto out;
445                 }
446         }
447
448 out:
449         if (buf)
450                 free(buf);
451         return rc;
452 }
453
454 /* See jt_obd_create */
455 static int obj_create(struct kid_t *kid)
456 {
457         struct obd_ioctl_data data;
458         int rc;
459
460         memset(&data, 0, sizeof(data));
461         data.ioc_dev = kid->k_dev;
462         data.ioc_obdo1.o_mode = 0100644;
463         data.ioc_obdo1.o_id = 0;
464         data.ioc_obdo1.o_gr = 2;
465         data.ioc_obdo1.o_uid = 0;
466         data.ioc_obdo1.o_gid = 0;
467         data.ioc_obdo1.o_valid = OBD_MD_FLTYPE | OBD_MD_FLMODE |
468                 OBD_MD_FLID | OBD_MD_FLUID | OBD_MD_FLGID;
469
470         rc = obj_ioctl(OBD_IOC_CREATE, &data, 1);
471         if (rc) {
472                 fprintf(stderr, "%d: create (%d) %s\n",
473                         kid->k_id, rc, strerror(errno));
474                 return rc;
475         }
476
477         if (!(data.ioc_obdo1.o_valid & OBD_MD_FLID)) {
478                 fprintf(stderr, "%d: create oid not valid "LPX64"\n",
479                         kid->k_id, data.ioc_obdo1.o_valid);
480                 return rc;
481         }
482
483         kid->k_objid = data.ioc_obdo1.o_id;
484
485         if (o_verbose > 4)
486                 printf("%d: cr "LPX64"\n", kid->k_id, kid->k_objid);
487
488         return rc;
489 }
490
491 /* See jt_obd_destroy */
492 static int obj_delete(struct kid_t *kid)
493 {
494         struct obd_ioctl_data data;
495         int rc;
496
497         if (o_verbose > 4)
498                 printf("%d: del "LPX64"\n", kid->k_id, kid->k_objid);
499
500         memset(&data, 0, sizeof(data));
501         data.ioc_dev = kid->k_dev;
502         data.ioc_obdo1.o_id = kid->k_objid;
503         data.ioc_obdo1.o_mode = S_IFREG | 0644;
504         data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLMODE;
505
506         rc = obj_ioctl(OBD_IOC_DESTROY, &data, 1);
507         if (rc)
508                 fprintf(stderr, "%s-%d: can't destroy obj "LPX64" (%d)\n",
509                         cmdname, kid->k_id, kid->k_objid, rc);
510
511         kid->k_objid = 0;
512         return rc;
513 }
514
515 #define difftime(a, b)                                  \
516         ((a)->tv_sec - (b)->tv_sec +                    \
517          ((a)->tv_usec - (b)->tv_usec) / 1000000.0)
518
519 /* See jt_obd_test_brw */
520 static int obj_write(struct kid_t *kid)
521 {
522         struct obd_ioctl_data data;
523         struct timeval start;
524         __u64 count, len;
525         int rc = 0, i, pages = 0;
526
527         if (o_verbose > 4)
528                 printf("%d: wr "LPX64"\n", kid->k_id, kid->k_objid);
529
530         count = 10;
531         pages = 32;
532         len = pages * getpagesize();
533
534         memset(&data, 0, sizeof(data));
535         data.ioc_dev = kid->k_dev;
536         /* communicate the 'type' of brw test and batching to echo_client.
537          * don't start.  we'd love to refactor this lctl->echo_client
538          * interface */
539         data.ioc_pbuf1 = (void *)1;
540         data.ioc_plen1 = 1;
541
542         data.ioc_obdo1.o_id = kid->k_objid;
543         data.ioc_obdo1.o_mode = S_IFREG;
544         data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLTYPE | OBD_MD_FLMODE |
545                 OBD_MD_FLFLAGS;
546         data.ioc_obdo1.o_flags = OBD_FL_DEBUG_CHECK;
547         data.ioc_count = len;
548         data.ioc_offset = 0;
549
550         gettimeofday(&start, NULL);
551
552         for (i = 1; i <= count; i++) {
553                 data.ioc_obdo1.o_valid &= ~(OBD_MD_FLBLOCKS|OBD_MD_FLGRANT);
554                 rc = obj_ioctl(OBD_IOC_BRW_WRITE, &data, 0);
555                 if (rc) {
556                         fprintf(stderr, "%d: write %s\n", kid->k_id,
557                                 strerror(rc = errno));
558                         break;
559                 }
560
561                 data.ioc_offset += len;
562         }
563
564         if (!rc) {
565                 struct timeval end;
566                 double diff;
567
568                 gettimeofday(&end, NULL);
569                 diff = difftime(&end, &start);
570
571                 --i;
572
573                 pthread_mutex_lock(&m_count);
574                 write_bytes += i * len;
575                 pthread_mutex_unlock(&m_count);
576
577                 if (o_verbose > 4)
578                         printf("%d: wrote %dx%d pages in %.3fs (%.3f MB/s): %s",
579                                kid->k_id, i, pages, diff,
580                                ((double)i * len) / (diff * 1048576.0),
581                                ctime(&end.tv_sec));
582         }
583
584         if (rc)
585                 fprintf(stderr, "%s-%d: err test_brw obj "LPX64" (%d)\n",
586                         cmdname, kid->k_id, kid->k_objid, rc);
587         return rc;
588 }
589
590 static int do_work(struct kid_t *kid)
591 {
592         int rc = 0, err, iter = 0;
593
594         if (!(kid->k_cmd.c_flags & C_CREATE_EVERY))
595                 rc = obj_create(kid);
596
597         for (iter = 0; iter < kid->k_cmd.c_rpt; iter++) {
598                 if (rc || sig_received)
599                         break;
600
601                 if (kid->k_cmd.c_flags & C_CREATE_EVERY) {
602                         rc = obj_create(kid);
603                         if (rc)
604                                 break;
605                 }
606
607                 if (kid->k_cmd.c_flags & C_WRITE) {
608                         rc = obj_write(kid);
609                         grant_estimate(kid->k_id);
610                 }
611
612                 if (kid->k_cmd.c_flags & C_CREATE_EVERY) {
613                         err = obj_delete(kid);
614                         if (!rc) rc = err;
615                 }
616
617                 if ((o_verbose > 3) && (iter % 10 == 0))
618                         printf("%d: i%d\n", kid->k_id, iter);
619                 if (!rc)
620                         sleep(kid->k_cmd.c_delay);
621         }
622
623         if (!(kid->k_cmd.c_flags & C_CREATE_EVERY)) {
624                 err = obj_delete(kid);
625                 if (!rc) rc = err;
626         }
627
628         if (o_verbose > 2)
629                 printf("%d: done (%d)\n", kid->k_id, rc);
630
631         return rc;
632 }
633
634 static void report_perf()
635 {
636         struct timeval end;
637         double diff;
638
639         gettimeofday(&end, NULL);
640         diff = difftime(&end, &trigger_start);
641         if (o_verbose > 2) {
642                 pthread_mutex_lock(&m_count);
643                 printf("wrote %lluMB in %.3fs (%.3f MB/s)\n",
644                        write_bytes >> 20, diff,
645                        (write_bytes >> 20) / diff);
646                 pthread_mutex_unlock(&m_count);
647         }
648 }
649
650 static void *run_one_child(void *threadvp)
651 {
652         struct kid_t *kid;
653         char oname[10], ename[10];
654         int thread = (long)threadvp, dev;
655         int rc = 0, err;
656
657         if (o_verbose > 2)
658                 printf("%s: running thread #%d\n", cmdname, thread);
659
660         sprintf(oname, "o%.5d", thread);
661         sprintf(ename, "e%.5d", thread);
662         rc = echocli_setup(oname, ename, &dev);
663         if (rc) {
664                 fprintf(stderr, "%s: can't setup '%s/%s' (%d)\n",
665                         cmdname, oname, ename, rc);
666                 pthread_exit((void *)(long)rc);
667         }
668
669         kid = push_kid(thread);
670         if (!kid) {
671                 rc = -ENOMEM;
672                 goto out;
673         }
674         kid->k_dev = dev;
675
676         while(!(rc || sig_received)) {
677                 pthread_mutex_lock(&m_trigger);
678                 waiting_count++;
679                 if ((waiting_count == live_threads) && timer_on) {
680                         report_perf();
681                         timer_on = 0;
682                         all_done = 1;
683                 }
684                 pthread_cond_wait(&cv_trigger, &m_trigger);
685                 waiting_count--;
686                 all_done = 0;
687
688                 /* First trigger_count threads will do the work, the rest
689                    will block again */
690                 if (trigger_count) {
691                         if (o_verbose > 4)
692                                 printf("%d: trigger %d cmd %x\n",
693                                        kid->k_id, trigger_count,
694                                        trigger_cmd.c_flags);
695                         trigger_count--;
696                         memcpy(&kid->k_cmd, &trigger_cmd, sizeof(trigger_cmd));
697                         pthread_mutex_unlock(&m_trigger);
698                         rc = do_work(kid);
699                 } else {
700                         pthread_mutex_unlock(&m_trigger);
701                 }
702         }
703
704         if (o_verbose > 1)
705                 printf("%s: thread #%d done (%d)\n", cmdname, thread, rc);
706
707         if (rc)
708                 stop_all(SIGINT);
709
710 out:
711         err = cleanup(ename, 0);
712         if (!rc) rc = err;
713         err = cleanup(oname, 0);
714         if (!rc) rc = err;
715
716         pthread_exit((void *)(long)rc);
717 }
718
719 static int loadgen_start_clients(int argc, char **argv)
720 {
721         int rc = 0, i, numt;
722         struct timespec ts = {0, 1000*1000*100}; /* .1 sec */
723         pthread_attr_t attr;
724
725         if (argc != 2)
726                 return CMD_HELP;
727
728         numt = strtoul(argv[1], NULL, 0);
729         if (numt < 1)
730                 return CMD_HELP;
731
732         if (!target[0]) {
733                 fprintf(stderr, "%s: target OST is not defined, use 'device' "
734                         "command\n", cmdname);
735                 return -EINVAL;
736         }
737
738         rc = pthread_attr_init(&attr);
739         if (rc) {
740                 fprintf(stderr, "%s: pthread_attr_init:(%d) %s\n",
741                         cmdname, rc, strerror(errno));
742                 return -errno;
743         }
744         pthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
745         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
746
747         numt += live_threads;
748         i = live_threads;
749         printf("start %d to %d\n", i, numt);
750         while(!rc && !sig_received && (i < numt)) {
751                 pthread_t thread;
752
753                 i++;
754                 rc = pthread_create(&thread, &attr, run_one_child,
755                                     (void *)(long)i);
756                 if (rc) {
757                         fprintf(stderr, "%s: pthread: #%d - (%d) %s\n",
758                                 cmdname, i, rc, strerror(rc));
759                         break;
760                 }
761
762                 /* give them slightly different start times */
763                 nanosleep(&ts, NULL);
764         }
765
766         pthread_attr_destroy(&attr);
767
768         return -rc;
769 }
770
771 static int loadgen_target(int argc, char **argv)
772 {
773         char *args[3];
774         __u64 nidx = 0;
775         int rc = 0;
776
777         if (argc < 2 || argc > 3)
778                 return CMD_HELP;
779
780         args[0] = cmdname;
781
782         if (argc == 3) {
783                 nidx = libcfs_str2nid(argv[2]);
784                 if (nidx == LNET_NID_ANY) {
785                         fprintf(stderr, "%s: invalid nid '%s'\n",
786                                 cmdname, argv[2]);
787                         return -EINVAL;
788                 }
789         } else {
790                 /* Local device should be in list */
791                 args[1] = argv[1];
792                 rc = jt_obd_device(2, args);
793                 if (rc) {
794                         fprintf(stderr, "%s: local device '%s' doesn't "
795                                 "seem to exist. You must use obdfilter device "
796                                 "names like 'lustre-OST0000'.  Use 'dl' to "
797                                 "list all devices.\n",
798                                 cmdname, argv[1]);
799                         return -EINVAL;
800                 }
801
802                 /* Use the first local nid */
803                 args[1] = (char *)(&nidx);
804                 args[1][0] = 1; /* hack to get back first nid */
805                 rc = jt_ptl_list_nids(2, args);
806                 if (rc) {
807                         fprintf(stderr, "%s: can't get local nid (%d)\n",
808                                 cmdname, rc);
809                         return rc;
810                 }
811         }
812         if (strcmp(nid, libcfs_nid2str(nidx)) != 0) {
813                 /* if new, do an add_uuid */
814                 sprintf(nid, "%s", libcfs_nid2str(nidx));
815
816                 /* Fixme change the uuid for every new one */
817                 args[1] = "OSS_UUID";
818                 args[2] = nid;
819                 rc = jt_lcfg_add_uuid(3, args);
820                 if (rc) {
821                         fprintf(stderr, "%s: can't add uuid '%s' (%d)\n",
822                                 cmdname, args[2], rc);
823                         return rc;
824                 }
825         }
826
827         snprintf(target, sizeof(target), "%s", argv[1]);
828         printf("Target OST name is '%s'\n", target);
829
830         return rc;
831 }
832
833 static int loadgen_verbose(int argc, char **argv)
834 {
835         if (argc != 2)
836                 return CMD_HELP;
837         o_verbose = atoi(argv[1]);
838         printf("verbosity set to %d\n", o_verbose);
839         return 0;
840 }
841
842 static int loadgen_write(int argc, char **argv)
843 {
844         int threads;
845
846         if (argc < 3 || argc > 4)
847                 return CMD_HELP;
848         threads = atoi(argv[1]);
849         if (threads > live_threads) {
850                 fprintf(stderr, "requested %d threads but only %d are running. "
851                         "Use 'start' to start some more.\n",
852                         threads, live_threads);
853                 return -EOVERFLOW;
854         }
855         trigger(C_WRITE, threads, atoi(argv[2]), 
856                 (argc == 4) ? atoi(argv[3]) : 0);
857         return 0;
858 }
859
860 char ecsname[] = "echosrv";
861 static int loadgen_stop_echosrv(int argc, char **argv)
862 {
863         int verbose = (argc != 9);
864         if (my_oss) {
865                 char name[]="OSS";
866                 cleanup(name, verbose);
867                 my_oss = 0;
868         }
869         if (my_ecs || (argc == 9)) {
870                 cleanup(ecsname, verbose);
871                 my_ecs = 0;
872         }
873         return 0;
874 }
875
876 static int loadgen_start_echosrv(int argc, char **argv)
877 {
878         char *args[5];
879         int rc;
880
881         pthread_mutex_lock(&m_config);
882
883         args[0] = cmdname;
884
885         /* attach obdecho echosrv echosrv_UUID */
886         args[1] = "obdecho";
887         args[2] = args[3] = ecsname;
888         rc = jt_lcfg_attach(4, args);
889         if (rc) {
890                 fprintf(stderr, "%s: can't attach echo server (%d)\n",
891                         cmdname, rc);
892                 /* Assume we want e.g. an old one cleaned anyhow. */
893                 goto clean;
894         }
895         my_ecs = 1;
896
897         /* setup */
898         rc = jt_lcfg_setup(1, args);
899         if (rc) {
900                 fprintf(stderr, "%s: can't setup echo server (%d)\n",
901                         cmdname, rc);
902                 goto clean;
903         }
904   
905         /* Create an OSS to handle the communications */
906         /* attach ost OSS OSS_UUID */
907         args[1] = "ost";
908         args[2] = args[3] = "OSS";
909
910         rc = jt_lcfg_attach(4, args);
911         if (rc == EEXIST) {
912                 /* Already set up for somebody else, that's fine. */
913                 printf("OSS already set up, no problem.\n");
914                 pthread_mutex_unlock(&m_config);
915                 return 0;
916         }
917         if (rc) {
918                 fprintf(stderr, "%s: can't attach OSS (%d)\n",
919                         cmdname, rc);
920                 goto clean;
921         }
922         my_oss = 1;
923
924         /* setup */
925         rc = jt_lcfg_setup(1, args);
926         if (rc) {
927                 fprintf(stderr, "%s: can't setup OSS (%d)\n",
928                         cmdname, rc);
929                 goto clean;
930         }
931
932         pthread_mutex_unlock(&m_config);
933         return rc;
934
935 clean:
936         pthread_mutex_unlock(&m_config);
937         loadgen_stop_echosrv(9, argv);
938         return rc;
939 }
940
941 static int loadgen_wait(int argc, char **argv)
942 {
943         /* Give scripts a chance to start some threads */   
944         sleep(1);
945         while (!all_done) {
946                 sleep(1);
947         }
948         return 0;
949 }
950
951 static int loadgen_init(int argc, char **argv)
952 {
953         char *args[3];
954         int rc;
955
956         sprintf(cmdname, "%s", argv[0]);
957
958         signal(SIGTERM, sig_master);
959         signal(SIGINT, sig_master);
960
961         /* Test to make sure obdecho module is loaded */
962         args[0] = cmdname;
963         args[1] = "echo_client";
964         args[2] = args[3] = "ecc_test";
965         rc = jt_lcfg_attach(4, args);
966         if (rc) {
967                 fprintf(stderr, "%s: can't attach echo client (%d)\n",
968                         cmdname, rc);
969                 if (rc == ENODEV)
970                         fprintf(stderr, "%s: is the obdecho module loaded?\n",
971                                 cmdname);
972         } else {
973                 args[1] = args[2];
974                 jt_obd_detach(1, args);
975         }
976
977         return rc;
978 }
979
980 static int loadgen_exit()
981 {
982         int rc;
983         
984         printf("stopping %d children\n", live_threads);
985         kill_kids();
986         rc = wait_for_threads();
987
988         loadgen_stop_echosrv(0, NULL);
989
990         return rc;
991 }
992
993 /* libptlctl interface */
994 static int loadgen_main(int argc, char **argv)
995 {
996         int rc;
997
998         setlinebuf(stdout);
999         /* without this threaded errors cause segfault */
1000         setlinebuf(stderr);
1001
1002         if ((rc = ptl_initialize(argc, argv)) < 0)
1003                 exit(rc);
1004         if ((rc = obd_initialize(argc, argv)) < 0)
1005                 exit(rc);
1006         if ((rc = dbg_initialize(argc, argv)) < 0)
1007                 exit(rc);
1008
1009         Parser_init("loadgen> ", cmdlist);
1010
1011         rc = loadgen_init(argc, argv);
1012         if (rc)
1013                 goto out;
1014
1015         if (argc > 1) {
1016                 rc = Parser_execarg(argc - 1, argv + 1, cmdlist);
1017         } else {
1018                 rc = Parser_commands();
1019         }
1020
1021         rc = loadgen_exit();
1022
1023 out:
1024         obd_finalize(argc, argv);
1025         return rc;
1026 }
1027
1028 #ifndef LIBLUSTRE_TEST
1029 int main (int argc, char **argv)
1030 {
1031         int rc;
1032         rc = loadgen_main(argc, argv);
1033         pthread_exit((void *)(long)rc);
1034
1035         return rc;
1036 }
1037 #endif
1038