Whamcloud - gitweb
catch extra args without dashes
[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, 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_uid = 0;
465         data.ioc_obdo1.o_gid = 0;
466         data.ioc_obdo1.o_valid = OBD_MD_FLTYPE | OBD_MD_FLMODE |
467                 OBD_MD_FLID | OBD_MD_FLUID | OBD_MD_FLGID;
468
469         rc = obj_ioctl(OBD_IOC_CREATE, &data, 1);
470         if (rc) {
471                 fprintf(stderr, "%d: create (%d) %s\n",
472                         kid->k_id, rc, strerror(errno));
473                 return rc;
474         }
475
476         if (!(data.ioc_obdo1.o_valid & OBD_MD_FLID)) {
477                 fprintf(stderr, "%d: create oid not valid "LPX64"\n",
478                         kid->k_id, data.ioc_obdo1.o_valid);
479                 return rc;
480         }
481
482         kid->k_objid = data.ioc_obdo1.o_id;
483
484         if (o_verbose > 4)
485                 printf("%d: cr "LPX64"\n", kid->k_id, kid->k_objid);
486
487         return rc;
488 }
489
490 /* See jt_obd_destroy */
491 static int obj_delete(struct kid_t *kid)
492 {
493         struct obd_ioctl_data data;
494         int rc;
495
496         if (o_verbose > 4)
497                 printf("%d: del "LPX64"\n", kid->k_id, kid->k_objid);
498
499         memset(&data, 0, sizeof(data));
500         data.ioc_dev = kid->k_dev;
501         data.ioc_obdo1.o_id = kid->k_objid;
502         data.ioc_obdo1.o_mode = S_IFREG | 0644;
503         data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLMODE;
504
505         rc = obj_ioctl(OBD_IOC_DESTROY, &data, 1);
506         if (rc)
507                 fprintf(stderr, "%s-%d: can't destroy obj "LPX64" (%d)\n",
508                         cmdname, kid->k_id, kid->k_objid, rc);
509
510         kid->k_objid = 0;
511         return rc;
512 }
513
514 #define difftime(a, b)                                  \
515         ((a)->tv_sec - (b)->tv_sec +                    \
516          ((a)->tv_usec - (b)->tv_usec) / 1000000.0)
517
518 /* See jt_obd_test_brw */
519 static int obj_write(struct kid_t *kid)
520 {
521         struct obd_ioctl_data data;
522         struct timeval start;
523         __u64 count, len;
524         int rc = 0, i, pages = 0;
525
526         if (o_verbose > 4)
527                 printf("%d: wr "LPX64"\n", kid->k_id, kid->k_objid);
528
529         count = 10;
530         pages = 32;
531         len = pages * getpagesize();
532
533         memset(&data, 0, sizeof(data));
534         data.ioc_dev = kid->k_dev;
535         /* communicate the 'type' of brw test and batching to echo_client.
536          * don't start.  we'd love to refactor this lctl->echo_client
537          * interface */
538         data.ioc_pbuf1 = (void *)1;
539         data.ioc_plen1 = 1;
540
541         data.ioc_obdo1.o_id = kid->k_objid;
542         data.ioc_obdo1.o_mode = S_IFREG;
543         data.ioc_obdo1.o_valid = OBD_MD_FLID | OBD_MD_FLTYPE | OBD_MD_FLMODE |
544                 OBD_MD_FLFLAGS;
545         data.ioc_obdo1.o_flags = OBD_FL_DEBUG_CHECK;
546         data.ioc_count = len;
547         data.ioc_offset = 0;
548
549         gettimeofday(&start, NULL);
550
551         for (i = 1; i <= count; i++) {
552                 data.ioc_obdo1.o_valid &= ~(OBD_MD_FLBLOCKS|OBD_MD_FLGRANT);
553                 rc = obj_ioctl(OBD_IOC_BRW_WRITE, &data, 0);
554                 if (rc) {
555                         fprintf(stderr, "%d: write %s\n", kid->k_id,
556                                 strerror(rc = errno));
557                         break;
558                 }
559
560                 data.ioc_offset += len;
561         }
562
563         if (!rc) {
564                 struct timeval end;
565                 double diff;
566
567                 gettimeofday(&end, NULL);
568                 diff = difftime(&end, &start);
569
570                 --i;
571
572                 pthread_mutex_lock(&m_count);
573                 write_bytes += i * len;
574                 pthread_mutex_unlock(&m_count);
575
576                 if (o_verbose > 4)
577                         printf("%d: wrote %dx%d pages in %.3fs (%.3f MB/s): %s",
578                                kid->k_id, i, pages, diff,
579                                ((double)i * len) / (diff * 1048576.0),
580                                ctime(&end.tv_sec));
581         }
582
583         if (rc)
584                 fprintf(stderr, "%s-%d: err test_brw obj "LPX64" (%d)\n",
585                         cmdname, kid->k_id, kid->k_objid, rc);
586         return rc;
587 }
588
589 static int do_work(struct kid_t *kid)
590 {
591         int rc = 0, err, iter = 0;
592
593         if (!(kid->k_cmd.c_flags & C_CREATE_EVERY))
594                 rc = obj_create(kid);
595
596         for (iter = 0; iter < kid->k_cmd.c_rpt; iter++) {
597                 if (rc || sig_received)
598                         break;
599
600                 if (kid->k_cmd.c_flags & C_CREATE_EVERY) {
601                         rc = obj_create(kid);
602                         if (rc)
603                                 break;
604                 }
605
606                 if (kid->k_cmd.c_flags & C_WRITE) {
607                         rc = obj_write(kid);
608                         grant_estimate(kid->k_id);
609                 }
610
611                 if (kid->k_cmd.c_flags & C_CREATE_EVERY) {
612                         err = obj_delete(kid);
613                         if (!rc) rc = err;
614                 }
615
616                 if ((o_verbose > 3) && (iter % 10 == 0))
617                         printf("%d: i%d\n", kid->k_id, iter);
618                 if (!rc)
619                         sleep(kid->k_cmd.c_delay);
620         }
621
622         if (!(kid->k_cmd.c_flags & C_CREATE_EVERY)) {
623                 err = obj_delete(kid);
624                 if (!rc) rc = err;
625         }
626
627         if (o_verbose > 2)
628                 printf("%d: done (%d)\n", kid->k_id, rc);
629
630         return rc;
631 }
632
633 static void report_perf()
634 {
635         struct timeval end;
636         double diff;
637
638         gettimeofday(&end, NULL);
639         diff = difftime(&end, &trigger_start);
640         if (o_verbose > 2) {
641                 pthread_mutex_lock(&m_count);
642                 printf("wrote %lluMB in %.3fs (%.3f MB/s)\n",
643                        write_bytes >> 20, diff,
644                        (write_bytes >> 20) / diff);
645                 pthread_mutex_unlock(&m_count);
646         }
647 }
648
649 static void *run_one_child(void *threadvp)
650 {
651         struct kid_t *kid;
652         char oname[10], ename[10];
653         int thread = (long)threadvp, dev;
654         int rc = 0, err;
655
656         if (o_verbose > 2)
657                 printf("%s: running thread #%d\n", cmdname, thread);
658
659         sprintf(oname, "o%.5d", thread);
660         sprintf(ename, "e%.5d", thread);
661         rc = echocli_setup(oname, ename, &dev);
662         if (rc) {
663                 fprintf(stderr, "%s: can't setup '%s/%s' (%d)\n",
664                         cmdname, oname, ename, rc);
665                 pthread_exit((void *)(long)rc);
666         }
667
668         kid = push_kid(thread);
669         if (!kid) {
670                 rc = -ENOMEM;
671                 goto out;
672         }
673         kid->k_dev = dev;
674
675         while(!(rc || sig_received)) {
676                 pthread_mutex_lock(&m_trigger);
677                 waiting_count++;
678                 if ((waiting_count == live_threads) && timer_on) {
679                         report_perf();
680                         timer_on = 0;
681                         all_done = 1;
682                 }
683                 pthread_cond_wait(&cv_trigger, &m_trigger);
684                 waiting_count--;
685                 all_done = 0;
686
687                 /* First trigger_count threads will do the work, the rest
688                    will block again */
689                 if (trigger_count) {
690                         if (o_verbose > 4)
691                                 printf("%d: trigger %d cmd %x\n",
692                                        kid->k_id, trigger_count,
693                                        trigger_cmd.c_flags);
694                         trigger_count--;
695                         memcpy(&kid->k_cmd, &trigger_cmd, sizeof(trigger_cmd));
696                         pthread_mutex_unlock(&m_trigger);
697                         rc = do_work(kid);
698                 } else {
699                         pthread_mutex_unlock(&m_trigger);
700                 }
701         }
702
703         if (o_verbose > 1)
704                 printf("%s: thread #%d done (%d)\n", cmdname, thread, rc);
705
706         if (rc)
707                 stop_all(SIGINT);
708
709 out:
710         err = cleanup(ename, 0);
711         if (!rc) rc = err;
712         err = cleanup(oname, 0);
713         if (!rc) rc = err;
714
715         pthread_exit((void *)(long)rc);
716 }
717
718 static int loadgen_start_clients(int argc, char **argv)
719 {
720         int rc = 0, i, numt;
721         struct timespec ts = {0, 1000*1000*100}; /* .1 sec */
722         pthread_attr_t attr;
723
724         if (argc != 2)
725                 return CMD_HELP;
726
727         numt = strtoul(argv[1], NULL, 0);
728         if (numt < 1)
729                 return CMD_HELP;
730
731         if (!target[0]) {
732                 fprintf(stderr, "%s: target OST is not defined, use 'device' "
733                         "command\n", cmdname);
734                 return -EINVAL;
735         }
736
737         rc = pthread_attr_init(&attr);
738         if (rc) {
739                 fprintf(stderr, "%s: pthread_attr_init:(%d) %s\n",
740                         cmdname, rc, strerror(errno));
741                 return -errno;
742         }
743         pthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
744         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
745
746         numt += live_threads;
747         i = live_threads;
748         printf("start %d to %d\n", i, numt);
749         while(!rc && !sig_received && (i < numt)) {
750                 pthread_t thread;
751
752                 i++;
753                 rc = pthread_create(&thread, &attr, run_one_child,
754                                     (void *)(long)i);
755                 if (rc) {
756                         fprintf(stderr, "%s: pthread: #%d - (%d) %s\n",
757                                 cmdname, i, rc, strerror(rc));
758                         break;
759                 }
760
761                 /* give them slightly different start times */
762                 nanosleep(&ts, NULL);
763         }
764
765         pthread_attr_destroy(&attr);
766
767         return -rc;
768 }
769
770 static int loadgen_target(int argc, char **argv)
771 {
772         char *args[3];
773         __u64 nidx = 0;
774         int rc = 0;
775
776         if (argc < 2 || argc > 3)
777                 return CMD_HELP;
778
779         args[0] = cmdname;
780
781         if (argc == 3) {
782                 nidx = libcfs_str2nid(argv[2]);
783                 if (nidx == LNET_NID_ANY) {
784                         fprintf(stderr, "%s: invalid nid '%s'\n",
785                                 cmdname, argv[2]);
786                         return -EINVAL;
787                 }
788         } else {
789                 /* Local device should be in list */
790                 args[1] = argv[1];
791                 rc = jt_obd_device(2, args);
792                 if (rc) {
793                         fprintf(stderr, "%s: local device '%s' doesn't "
794                                 "seem to exist. You must use obdfilter device "
795                                 "names like 'lustre-OST0000'.  Use 'dl' to "
796                                 "list all devices.\n",
797                                 cmdname, argv[1]);
798                         return -EINVAL;
799                 }
800
801                 /* Use the first local nid */
802                 args[1] = (char *)(&nidx);
803                 args[1][0] = 1; /* hack to get back first nid */
804                 rc = jt_ptl_list_nids(2, args);
805                 if (rc) {
806                         fprintf(stderr, "%s: can't get local nid (%d)\n",
807                                 cmdname, rc);
808                         return rc;
809                 }
810         }
811         if (strcmp(nid, libcfs_nid2str(nidx)) != 0) {
812                 /* if new, do an add_uuid */
813                 sprintf(nid, "%s", libcfs_nid2str(nidx));
814
815                 /* Fixme change the uuid for every new one */
816                 args[1] = "OSS_UUID";
817                 args[2] = nid;
818                 rc = jt_lcfg_add_uuid(3, args);
819                 if (rc) {
820                         fprintf(stderr, "%s: can't add uuid '%s' (%d)\n",
821                                 cmdname, args[2], rc);
822                         return rc;
823                 }
824         }
825
826         snprintf(target, sizeof(target), "%s", argv[1]);
827         printf("Target OST name is '%s'\n", target);
828
829         return rc;
830 }
831
832 static int loadgen_verbose(int argc, char **argv)
833 {
834         if (argc != 2)
835                 return CMD_HELP;
836         o_verbose = atoi(argv[1]);
837         printf("verbosity set to %d\n", o_verbose);
838         return 0;
839 }
840
841 static int loadgen_write(int argc, char **argv)
842 {
843         int threads;
844
845         if (argc < 3 || argc > 4)
846                 return CMD_HELP;
847         threads = atoi(argv[1]);
848         if (threads > live_threads) {
849                 fprintf(stderr, "requested %d threads but only %d are running. "
850                         "Use 'start' to start some more.\n",
851                         threads, live_threads);
852                 return -EOVERFLOW;
853         }
854         trigger(C_WRITE, threads, atoi(argv[2]), 
855                 (argc == 4) ? atoi(argv[3]) : 0);
856         return 0;
857 }
858
859 char ecsname[] = "echosrv";
860 static int loadgen_stop_echosrv(int argc, char **argv)
861 {
862         int verbose = (argc != 9);
863         if (my_oss) {
864                 char name[]="OSS";
865                 cleanup(name, verbose);
866                 my_oss = 0;
867         }
868         if (my_ecs || (argc == 9)) {
869                 cleanup(ecsname, verbose);
870                 my_ecs = 0;
871         }
872         return 0;
873 }
874
875 static int loadgen_start_echosrv(int argc, char **argv)
876 {
877         char *args[5];
878         int rc;
879
880         pthread_mutex_lock(&m_config);
881
882         args[0] = cmdname;
883
884         /* attach obdecho echosrv echosrv_UUID */
885         args[1] = "obdecho";
886         args[2] = args[3] = ecsname;
887         rc = jt_lcfg_attach(4, args);
888         if (rc) {
889                 fprintf(stderr, "%s: can't attach echo server (%d)\n",
890                         cmdname, rc);
891                 /* Assume we want e.g. an old one cleaned anyhow. */
892                 goto clean;
893         }
894         my_ecs = 1;
895
896         /* setup */
897         rc = jt_lcfg_setup(1, args);
898         if (rc) {
899                 fprintf(stderr, "%s: can't setup echo server (%d)\n",
900                         cmdname, rc);
901                 goto clean;
902         }
903   
904         /* Create an OSS to handle the communications */
905         /* attach ost OSS OSS_UUID */
906         args[1] = "ost";
907         args[2] = args[3] = "OSS";
908
909         rc = jt_lcfg_attach(4, args);
910         if (rc == EEXIST) {
911                 /* Already set up for somebody else, that's fine. */
912                 printf("OSS already set up, no problem.\n");
913                 pthread_mutex_unlock(&m_config);
914                 return 0;
915         }
916         if (rc) {
917                 fprintf(stderr, "%s: can't attach OSS (%d)\n",
918                         cmdname, rc);
919                 goto clean;
920         }
921         my_oss = 1;
922
923         /* setup */
924         rc = jt_lcfg_setup(1, args);
925         if (rc) {
926                 fprintf(stderr, "%s: can't setup OSS (%d)\n",
927                         cmdname, rc);
928                 goto clean;
929         }
930
931         pthread_mutex_unlock(&m_config);
932         return rc;
933
934 clean:
935         pthread_mutex_unlock(&m_config);
936         loadgen_stop_echosrv(9, argv);
937         return rc;
938 }
939
940 static int loadgen_wait(int argc, char **argv)
941 {
942         /* Give scripts a chance to start some threads */   
943         sleep(1);
944         while (!all_done) {
945                 sleep(1);
946         }
947         return 0;
948 }
949
950 static int loadgen_init(int argc, char **argv)
951 {
952         char *args[3];
953         int rc;
954
955         sprintf(cmdname, "%s", argv[0]);
956
957         signal(SIGTERM, sig_master);
958         signal(SIGINT, sig_master);
959
960         /* Test to make sure obdecho module is loaded */
961         args[0] = cmdname;
962         args[1] = "echo_client";
963         args[2] = args[3] = "ecc_test";
964         rc = jt_lcfg_attach(4, args);
965         if (rc) {
966                 fprintf(stderr, "%s: can't attach echo client (%d)\n",
967                         cmdname, rc);
968                 if (rc == ENODEV)
969                         fprintf(stderr, "%s: is the obdecho module loaded?\n",
970                                 cmdname);
971         } else {
972                 args[1] = args[2];
973                 jt_obd_detach(1, args);
974         }
975
976         return rc;
977 }
978
979 static int loadgen_exit()
980 {
981         int rc;
982         
983         printf("stopping %d children\n", live_threads);
984         kill_kids();
985         rc = wait_for_threads();
986
987         loadgen_stop_echosrv(0, NULL);
988
989         return rc;
990 }
991
992 /* libptlctl interface */
993 static int loadgen_main(int argc, char **argv)
994 {
995         int rc;
996
997         setlinebuf(stdout);
998         /* without this threaded errors cause segfault */
999         setlinebuf(stderr);
1000
1001         if ((rc = ptl_initialize(argc, argv)) < 0)
1002                 exit(rc);
1003         if ((rc = obd_initialize(argc, argv)) < 0)
1004                 exit(rc);
1005         if ((rc = dbg_initialize(argc, argv)) < 0)
1006                 exit(rc);
1007
1008         Parser_init("loadgen> ", cmdlist);
1009
1010         rc = loadgen_init(argc, argv);
1011         if (rc)
1012                 goto out;
1013
1014         if (argc > 1) {
1015                 rc = Parser_execarg(argc - 1, argv + 1, cmdlist);
1016         } else {
1017                 rc = Parser_commands();
1018         }
1019
1020         rc = loadgen_exit();
1021
1022 out:
1023         obd_finalize(argc, argv);
1024         return rc;
1025 }
1026
1027 #ifndef LIBLUSTRE_TEST
1028 int main (int argc, char **argv)
1029 {
1030         int rc;
1031         rc = loadgen_main(argc, argv);
1032         pthread_exit((void *)(long)rc);
1033
1034         return rc;
1035 }
1036 #endif
1037