diff --git a/oclengine.c b/oclengine.c index c649003..552e8e9 100644 --- a/oclengine.c +++ b/oclengine.c @@ -50,6 +50,7 @@ #define round_up_pow2(x, a) (((x) + ((a)-1)) & ~((a)-1)) static void vg_ocl_free_args(vg_ocl_context_t *vocp); +static void *vg_opencl_loop(vg_exec_context_t *arg); /* OpenCL address searching mode */ @@ -872,6 +873,7 @@ vg_ocl_init(vg_context_t *vcp, vg_ocl_context_t *vocp, cl_device_id did, memset(vocp, 0, sizeof(*vocp)); vg_exec_context_init(vcp, &vocp->base); + vocp->base.vxc_threadfunc = vg_opencl_loop; pthread_mutex_init(&vocp->voc_lock, NULL); pthread_cond_init(&vocp->voc_wait, NULL); @@ -1872,8 +1874,8 @@ out: * Address search thread main loop */ -void * -vg_opencl_loop(void *arg) +static void * +vg_opencl_loop(vg_exec_context_t *arg) { vg_ocl_context_t *vocp = (vg_ocl_context_t *) arg; int i; @@ -2397,7 +2399,7 @@ get_platform(int num) } void -enumerate_opencl(void) +vg_ocl_enumerate_devices(void) { cl_platform_id *pids; cl_device_id *dids; @@ -2420,7 +2422,7 @@ enumerate_opencl(void) } } -cl_device_id +static cl_device_id get_opencl_device(int platformidx, int deviceidx) { cl_platform_id pid; @@ -2432,7 +2434,6 @@ get_opencl_device(int platformidx, int deviceidx) if (did) return did; } - enumerate_opencl(); return NULL; } @@ -2603,6 +2604,96 @@ out_fail: return NULL; } +vg_ocl_context_t * +vg_ocl_context_new_from_devstr(vg_context_t *vcp, const char *devstr, + int safemode, int verify) +{ + int platformidx, deviceidx; + int worksize = 0, nthreads = 0, nrows = 0, ncols = 0, invsize = 0; + + char *dsd, *part, *part2, *save, *param; + + dsd = strdup(devstr); + if (!dsd) + return NULL; + + save = NULL; + part = strtok_r(dsd, ",", &save); + + part2 = strchr(part, ':'); + if (!part2) { + fprintf(stderr, "Invalid device specifier '%s'\n", part); + free(dsd); + return NULL; + } + + *part2 = '\0'; + platformidx = atoi(part); + deviceidx = atoi(part2 + 1); + + while ((part = strtok_r(NULL, ",", &save)) != NULL) { + param = strchr(part, '='); + if (!param) { + fprintf(stderr, "Unrecognized parameter '%s'\n", part); + continue; + } + + *param = '\0'; + param++; + + if (!strcmp(part, "grid")) { + ncols = strtol(param, &part2, 0); + if (part2 && *part2 == 'x') { + nrows = strtol(part2+1, NULL, 0); + } + if (!nrows || !ncols) { + fprintf(stderr, + "Invalid grid size '%s'\n", param); + nrows = 0; + ncols = 0; + continue; + } + } + + else if (!strcmp(part, "invsize")) { + invsize = atoi(param); + if (!invsize) { + fprintf(stderr, + "Invalid modular inverse size '%s'\n", + param); + continue; + } + if (invsize & (invsize - 1)) { + fprintf(stderr, + "Modular inverse size %d must be " + "a power of 2\n", invsize); + invsize = 0; + continue; + } + } + + else if (!strcmp(part, "threads")) { + nthreads = atoi(param); + if (nthreads == 0) { + fprintf(stderr, + "Invalid thread count '%s'\n", optarg); + continue; + } + } + + else { + fprintf(stderr, "Unrecognized parameter '%s'\n", part); + } + } + + free(dsd); + + return vg_ocl_context_new(vcp, platformidx, deviceidx, safemode, + verify, worksize, nthreads, nrows, ncols, + invsize); +} + + void vg_ocl_context_free(vg_ocl_context_t *vocp) { diff --git a/oclengine.h b/oclengine.h index d13c1f0..a49fb98 100644 --- a/oclengine.h +++ b/oclengine.h @@ -30,6 +30,10 @@ extern vg_ocl_context_t *vg_ocl_context_new( int invsize); extern void vg_ocl_context_free(vg_ocl_context_t *vocp); -extern void *vg_opencl_loop(void *vocp); +extern vg_ocl_context_t *vg_ocl_context_new_from_devstr( + vg_context_t *vcp, const char *devstr, int safemode, int verify) +; + +extern void vg_ocl_enumerate_devices(void); #endif /* !defined (__VG_OCLENGINE_H__) */ diff --git a/oclvanitygen.c b/oclvanitygen.c index 794c841..2faed9e 100644 --- a/oclvanitygen.c +++ b/oclvanitygen.c @@ -45,6 +45,11 @@ usage(const char *name) "location or imported into a bitcoin client to spend any balance received on\n" "the address.\n" "By default, is interpreted as an exact prefix.\n" +"By default, if no device is specified, and the system has exactly one OpenCL\n" +"device, it will be selected automatically, otherwise if the system has\n" +"multiple OpenCL devices and no device is specified, an error will be\n" +"reported. To use multiple devices simultaneously, specify the -D option for\n" +"each device.\n" "\n" "Options:\n" "-v Verbose output\n" @@ -58,6 +63,9 @@ usage(const char *name) "-E Encrypt private keys with (UNSAFE)\n" "-p Select OpenCL platform\n" "-d Select OpenCL device\n" +"-D Use OpenCL device, identified by device string\n" +" Form: :[,]\n" +" Example: 0:0,grid=1024x1024\n" "-S Safe mode, disable OpenCL loop unrolling optimizations\n" "-w Set work items per thread in a work unit\n" "-t Set target thread count per multiprocessor\n" @@ -71,6 +79,8 @@ usage(const char *name) version, name); } +#define MAX_DEVS 32 + int main(int argc, char **argv) { @@ -99,9 +109,12 @@ main(int argc, char **argv) EC_POINT *pubkey_base = NULL; const char *result_file = NULL; const char *key_password = NULL; + char *devstrs[MAX_DEVS]; + int ndevstrs = 0; + int opened = 0; while ((opt = getopt(argc, argv, - "vqikNTX:eE:p:P:d:w:t:g:b:VSh?f:o:s:")) != -1) { + "vqikNTX:eE:p:P:d:w:t:g:b:VSh?f:o:s:D:")) != -1) { switch (opt) { case 'v': verbose = 2; @@ -188,6 +201,15 @@ main(int argc, char **argv) case 'S': safe_mode = 1; break; + case 'D': + if (ndevstrs >= MAX_DEVS) { + fprintf(stderr, + "Too many OpenCL devices (limit %d)\n", + MAX_DEVS); + return 1; + } + devstrs[ndevstrs++] = optarg; + break; case 'P': { if (pubkey_base != NULL) { fprintf(stderr, @@ -338,14 +360,38 @@ main(int argc, char **argv) fprintf(stderr, "Regular expressions: %ld\n", vcp->vc_npatterns); - vocp = vg_ocl_context_new(vcp, platformidx, deviceidx, - safe_mode, verify_mode, - worksize, nthreads, nrows, ncols, invsize); - if (!vocp) { + if (ndevstrs) { + for (opt = 0; opt < ndevstrs; opt++) { + vocp = vg_ocl_context_new_from_devstr(vcp, devstrs[opt], + safe_mode, + verify_mode); + if (!vocp) { + fprintf(stderr, + "Could not open device '%s', ignoring\n", + devstrs[opt]); + } else { + opened++; + } + } + } else { + vocp = vg_ocl_context_new(vcp, platformidx, deviceidx, + safe_mode, verify_mode, + worksize, nthreads, + nrows, ncols, invsize); + if (vocp) + opened++; + } + + if (!opened) { + vg_ocl_enumerate_devices(); return 1; } - vg_opencl_loop(vocp); + opt = vg_context_start_threads(vcp); + if (opt) + return 1; + + vg_context_wait_for_completion(vcp); vg_ocl_context_free(vocp); return 0; } diff --git a/oclvanityminer.c b/oclvanityminer.c index 6713691..19c9964 100644 --- a/oclvanityminer.c +++ b/oclvanityminer.c @@ -735,6 +735,11 @@ usage(const char *name) "generate the address with the best difficulty to reward ratio. Maintains\n" "contact with the bounty pool server and periodically refreshes the bounty\n" "list.\n" +"By default, if no device is specified, and the system has exactly one OpenCL\n" +"device, it will be selected automatically, otherwise if the system has\n" +"multiple OpenCL devices and no device is specified, an error will be\n" +"reported. To use multiple devices simultaneously, specify the -D option for\n" +"each device.\n" "\n" "Options:\n" "-u Bounty pool URL\n" @@ -744,6 +749,9 @@ usage(const char *name) "-q Quiet output\n" "-p Select OpenCL platform\n" "-d Select OpenCL device\n" +"-D Use OpenCL device, identified by device string\n" +" Form: :[,]\n" +" Example: 0:0,grid=1024x1024\n" "-S Safe mode, disable OpenCL loop unrolling optimizations\n" "-w Set work items per thread in a work unit\n" "-t Set target thread count per multiprocessor\n" @@ -753,6 +761,7 @@ usage(const char *name) version, name); } +#define MAX_DEVS 32 int main(int argc, char **argv) @@ -770,12 +779,15 @@ main(int argc, char **argv) int invsize = 0; int verify_mode = 0; int safe_mode = 0; + + char *devstrs[MAX_DEVS]; + int ndevstrs = 0; + vg_context_t *vcp = NULL; vg_ocl_context_t *vocp = NULL; int res; int thread_started = 0; - pthread_t thread; pubkeybatch_t *active_pkb = NULL; server_context_t *scp = NULL; @@ -794,7 +806,7 @@ main(int argc, char **argv) } while ((opt = getopt(argc, argv, - "u:a:vqp:d:w:t:g:b:VSh?i:")) != -1) { + "u:a:vqp:d:w:t:g:b:VD:Sh?i:")) != -1) { switch (opt) { case 'u': url = optarg; @@ -871,6 +883,15 @@ main(int argc, char **argv) case 'S': safe_mode = 1; break; + case 'D': + if (ndevstrs >= MAX_DEVS) { + fprintf(stderr, + "Too many OpenCL devices (limit %d)\n", + MAX_DEVS); + return 1; + } + devstrs[ndevstrs++] = optarg; + break; default: usage(argv[0]); return 1; @@ -915,6 +936,34 @@ main(int argc, char **argv) if (server_context_getwork(scp)) return 1; + /* Set up OpenCL */ + res = 0; + if (ndevstrs) { + for (opt = 0; opt < ndevstrs; opt++) { + vocp = vg_ocl_context_new_from_devstr(vcp, devstrs[opt], + safe_mode, + verify_mode); + if (!vocp) { + fprintf(stderr, + "Could not open device '%s', ignoring\n", + devstrs[opt]); + } else { + res++; + } + } + } else { + vocp = vg_ocl_context_new(vcp, platformidx, deviceidx, + safe_mode, verify_mode, + worksize, nthreads, + nrows, ncols, invsize); + if (vocp) + res++; + } + if (!res) { + vg_ocl_enumerate_devices(); + return 1; + } + while (1) { if (avl_root_empty(&scp->items)) server_context_getwork(scp); @@ -929,10 +978,8 @@ main(int argc, char **argv) if (thread_started && (!active_pkb || (pkb != active_pkb))) { /* If a thread is running, stop it */ - vcp->vc_halt = 1; - pthread_join(thread, NULL); + vg_context_stop_threads(vcp); thread_started = 0; - vcp->vc_halt = 0; if (active_pkb) { check_solution(scp, active_pkb); active_pkb = NULL; @@ -970,18 +1017,9 @@ main(int argc, char **argv) assert(vcp->vc_npatterns); } - if (!vocp) { - vocp = vg_ocl_context_new(vcp, - platformidx, deviceidx, - safe_mode, verify_mode, - worksize, nthreads, nrows, - ncols, invsize); - if (!vocp) - return 1; - } - - res = pthread_create(&thread, NULL, - vg_opencl_loop, vocp); + res = vg_context_start_threads(vcp); + if (res) + return 1; thread_started = 1; active_pkb = pkb; } diff --git a/pattern.c b/pattern.c index b6cf5ce..86bcab8 100644 --- a/pattern.c +++ b/pattern.c @@ -54,16 +54,14 @@ static pthread_mutex_t vg_thread_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t vg_thread_rdcond = PTHREAD_COND_INITIALIZER; static pthread_cond_t vg_thread_wrcond = PTHREAD_COND_INITIALIZER; static pthread_cond_t vg_thread_upcond = PTHREAD_COND_INITIALIZER; -static vg_exec_context_t *vg_threads = NULL; -static int vg_thread_excl = 0; static void __vg_exec_context_yield(vg_exec_context_t *vxcp) { vxcp->vxc_lockmode = 0; - while (vg_thread_excl) { + while (vxcp->vxc_vc->vc_thread_excl) { if (vxcp->vxc_stop) { - assert(vg_thread_excl); + assert(vxcp->vxc_vc->vc_thread_excl); vxcp->vxc_stop = 0; pthread_cond_signal(&vg_thread_upcond); } @@ -78,6 +76,7 @@ int vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp) { vg_exec_context_t *tp; + vg_context_t *vcp; if (vxcp->vxc_lockmode == 2) return 0; @@ -86,20 +85,21 @@ vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp) assert(vxcp->vxc_lockmode == 1); vxcp->vxc_lockmode = 0; + vcp = vxcp->vxc_vc; - if (vg_thread_excl++) { + if (vcp->vc_thread_excl++) { assert(vxcp->vxc_stop); vxcp->vxc_stop = 0; pthread_cond_signal(&vg_thread_upcond); pthread_cond_wait(&vg_thread_wrcond, &vg_thread_lock); - for (tp = vg_threads; tp != NULL; tp = tp->vxc_next) { + for (tp = vcp->vc_threads; tp != NULL; tp = tp->vxc_next) { assert(!tp->vxc_lockmode); assert(!tp->vxc_stop); } } else { - for (tp = vg_threads; tp != NULL; tp = tp->vxc_next) { + for (tp = vcp->vc_threads; tp != NULL; tp = tp->vxc_next) { if (tp->vxc_lockmode) { assert(tp->vxc_lockmode != 2); tp->vxc_stop = 1; @@ -107,7 +107,9 @@ vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp) } do { - for (tp = vg_threads; tp != NULL; tp = tp->vxc_next) { + for (tp = vcp->vc_threads; + tp != NULL; + tp = tp->vxc_next) { if (tp->vxc_lockmode) { assert(tp->vxc_lockmode != 2); pthread_cond_wait(&vg_thread_upcond, @@ -127,10 +129,9 @@ void vg_exec_context_downgrade_lock(vg_exec_context_t *vxcp) { pthread_mutex_lock(&vg_thread_lock); - assert(vxcp->vxc_lockmode == 2); assert(!vxcp->vxc_stop); - if (!--vg_thread_excl) { + if (!--vxcp->vxc_vc->vc_thread_excl) { vxcp->vxc_lockmode = 1; pthread_cond_broadcast(&vg_thread_rdcond); pthread_mutex_unlock(&vg_thread_lock); @@ -166,8 +167,8 @@ vg_exec_context_init(vg_context_t *vcp, vg_exec_context_t *vxcp) vxcp->vxc_lockmode = 0; vxcp->vxc_stop = 0; - vxcp->vxc_next = vg_threads; - vg_threads = vxcp; + vxcp->vxc_next = vcp->vc_threads; + vcp->vc_threads = vxcp; __vg_exec_context_yield(vxcp); pthread_mutex_unlock(&vg_thread_lock); return 1; @@ -185,7 +186,7 @@ vg_exec_context_del(vg_exec_context_t *vxcp) assert(vxcp->vxc_lockmode == 1); vxcp->vxc_lockmode = 0; - for (pprev = &vg_threads, tp = *pprev; + for (pprev = &vxcp->vxc_vc->vc_threads, tp = *pprev; (tp != vxcp) && (tp != NULL); pprev = &tp->vxc_next, tp = *pprev); @@ -634,6 +635,49 @@ vg_context_hash160_sort(vg_context_t *vcp, void *buf) return vcp->vc_hash160_sort(vcp, buf); } +int +vg_context_start_threads(vg_context_t *vcp) +{ + vg_exec_context_t *vxcp; + int res; + + for (vxcp = vcp->vc_threads; vxcp != NULL; vxcp = vxcp->vxc_next) { + res = pthread_create((pthread_t *) &vxcp->vxc_pthread, + NULL, + (void *(*)(void *)) vxcp->vxc_threadfunc, + vxcp); + if (res) { + fprintf(stderr, "ERROR: could not create thread: %d\n", + res); + vg_context_stop_threads(vcp); + return -1; + } + vxcp->vxc_thread_active = 1; + } + return 0; +} + +void +vg_context_stop_threads(vg_context_t *vcp) +{ + vcp->vc_halt = 1; + vg_context_wait_for_completion(vcp); + vcp->vc_halt = 0; +} + +void +vg_context_wait_for_completion(vg_context_t *vcp) +{ + vg_exec_context_t *vxcp; + + for (vxcp = vcp->vc_threads; vxcp != NULL; vxcp = vxcp->vxc_next) { + if (!vxcp->vxc_thread_active) + continue; + pthread_join((pthread_t) vxcp->vxc_pthread, NULL); + vxcp->vxc_thread_active = 0; + } +} + /* * Find the bignum ranges that produce a given prefix. diff --git a/pattern.h b/pattern.h index 95fbfae..b08ff46 100644 --- a/pattern.h +++ b/pattern.h @@ -22,6 +22,8 @@ #include #include +#include + #ifdef _WIN32 #include "winglue.h" #else @@ -35,11 +37,15 @@ #define VANITYGEN_VERSION "0.20pre" - typedef struct _vg_context_s vg_context_t; +struct _vg_exec_context_s; +typedef struct _vg_exec_context_s vg_exec_context_t; + +typedef void *(*vg_exec_context_threadfunc_t)(vg_exec_context_t *); + /* Context of one pattern-matching unit within the process */ -typedef struct _vg_exec_context_s { +struct _vg_exec_context_s { vg_context_t *vxc_vc; BN_CTX *vxc_bnctx; EC_KEY *vxc_key; @@ -50,23 +56,15 @@ typedef struct _vg_exec_context_s { BIGNUM vxc_bntmp; BIGNUM vxc_bntmp2; + vg_exec_context_threadfunc_t vxc_threadfunc; + pthread_t vxc_pthread; + int vxc_thread_active; + /* Thread synchronization */ struct _vg_exec_context_s *vxc_next; int vxc_lockmode; int vxc_stop; -} vg_exec_context_t; - -/* Init/cleanup for common execution context */ -extern int vg_exec_context_init(vg_context_t *vcp, vg_exec_context_t *vxcp); -extern void vg_exec_context_del(vg_exec_context_t *vxcp); -extern void vg_exec_context_consolidate_key(vg_exec_context_t *vxcp); -extern void vg_exec_context_calc_address(vg_exec_context_t *vxcp); -extern EC_KEY *vg_exec_context_new_key(void); - -/* Execution context lock handling functions */ -extern void vg_exec_context_downgrade_lock(vg_exec_context_t *vxcp); -extern int vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp); -extern void vg_exec_context_yield(vg_exec_context_t *vxcp); +}; typedef void (*vg_free_func_t)(vg_context_t *); @@ -106,6 +104,9 @@ struct _vg_context_s { EC_POINT *vc_pubkey_base; int vc_halt; + vg_exec_context_t *vc_threads; + int vc_thread_excl; + /* Internal methods */ vg_free_func_t vc_free; vg_add_pattern_func_t vc_add_patterns; @@ -126,26 +127,48 @@ struct _vg_context_s { }; +/* Base context methods */ extern void vg_context_free(vg_context_t *vcp); extern int vg_context_add_patterns(vg_context_t *vcp, const char ** const patterns, int npatterns); extern void vg_context_clear_all_patterns(vg_context_t *vcp); -extern int vg_context_hash160_sort(vg_context_t *vcp, void *buf); -extern void vg_context_thread_exit(vg_context_t *vcp); - +extern int vg_context_start_threads(vg_context_t *vcp); +extern void vg_context_stop_threads(vg_context_t *vcp); +extern void vg_context_wait_for_completion(vg_context_t *vcp); +/* Prefix context methods */ extern vg_context_t *vg_prefix_context_new(int addrtype, int privtype, int caseinsensitive); extern double vg_prefix_get_difficulty(int addrtype, const char *pattern); +/* Regex context methods */ extern vg_context_t *vg_regex_context_new(int addrtype, int privtype); +/* Utility functions */ extern int vg_output_timing(vg_context_t *vcp, int cycle, struct timeval *last); - extern void vg_output_match_console(vg_context_t *vcp, EC_KEY *pkey, const char *pattern); extern void vg_output_timing_console(vg_context_t *vcp, double count, unsigned long long rate, unsigned long long total); + + +/* Internal vg_context methods */ +extern int vg_context_hash160_sort(vg_context_t *vcp, void *buf); +extern void vg_context_thread_exit(vg_context_t *vcp); + +/* Internal Init/cleanup for common execution context */ +extern int vg_exec_context_init(vg_context_t *vcp, vg_exec_context_t *vxcp); +extern void vg_exec_context_del(vg_exec_context_t *vxcp); +extern void vg_exec_context_consolidate_key(vg_exec_context_t *vxcp); +extern void vg_exec_context_calc_address(vg_exec_context_t *vxcp); +extern EC_KEY *vg_exec_context_new_key(void); + +/* Internal execution context lock handling functions */ +extern void vg_exec_context_downgrade_lock(vg_exec_context_t *vxcp); +extern int vg_exec_context_upgrade_lock(vg_exec_context_t *vxcp); +extern void vg_exec_context_yield(vg_exec_context_t *vxcp); + + #endif /* !defined (__VG_PATTERN_H__) */