00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <stdlib.h>
00025 #include <stdio.h>
00026 #include <string.h>
00027 #include <glib.h>
00028 #include <sys/time.h>
00029 #include <sys/types.h>
00030 #include <sys/types.h>
00031 #include <sys/socket.h>
00032 #include <syslog.h>
00033 #include <unistd.h>
00034 #include "config.h"
00035 #include "lfs.h"
00036 #define MY_NAME "nbd-tester-client"
00037 #include "cliserv.h"
00038
00039 static gchar errstr[1024];
00040 const static int errstr_len=1024;
00041
00042 typedef enum {
00043 CONNECTION_TYPE_NONE,
00044 CONNECTION_TYPE_CONNECT,
00045 CONNECTION_TYPE_INIT_PASSWD,
00046 CONNECTION_TYPE_CLISERV,
00047 CONNECTION_TYPE_FULL,
00048 } CONNECTION_TYPE;
00049
00050 typedef enum {
00051 CONNECTION_CLOSE_PROPERLY,
00052 CONNECTION_CLOSE_FAST,
00053 } CLOSE_TYPE;
00054
00055 inline int read_all(int f, void *buf, size_t len) {
00056 ssize_t res;
00057 size_t retval=0;
00058
00059 while(len>0) {
00060 if((res=read(f, buf, len)) <=0) {
00061 snprintf(errstr, errstr_len, "Read failed: %s", strerror(errno));
00062 return -1;
00063 }
00064 len-=res;
00065 buf+=res;
00066 retval+=res;
00067 }
00068 return retval;
00069 }
00070
00071 int setup_connection(gchar *hostname, int port, CONNECTION_TYPE ctype) {
00072 int sock;
00073 struct hostent *host;
00074 struct sockaddr_in addr;
00075 char buf[256];
00076 u64 tmp64;
00077
00078 sock=0;
00079 if(ctype<CONNECTION_TYPE_CONNECT)
00080 goto end;
00081 if((sock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))<0) {
00082 strncpy(errstr, strerror(errno), errstr_len);
00083 goto err;
00084 }
00085 setmysockopt(sock);
00086 if(!(host=gethostbyname(hostname))) {
00087 strncpy(errstr, strerror(errno), errstr_len);
00088 goto err_open;
00089 }
00090 addr.sin_family=AF_INET;
00091 addr.sin_port=htons(port);
00092 addr.sin_addr.s_addr=*((int *) host->h_addr);
00093 if((connect(sock, (struct sockaddr *)&addr, sizeof(addr))<0)) {
00094 strncpy(errstr, strerror(errno), errstr_len);
00095 goto err_open;
00096 }
00097 if(ctype<CONNECTION_TYPE_INIT_PASSWD)
00098 goto end;
00099 if(read_all(sock, buf, strlen(INIT_PASSWD))<0) {
00100 snprintf(errstr, errstr_len, "Could not read INIT_PASSWD: %s",
00101 strerror(errno));
00102 goto err_open;
00103 }
00104 if(strlen(buf)==0) {
00105 snprintf(errstr, errstr_len, "Server closed connection");
00106 goto err_open;
00107 }
00108 if(strncmp(buf, INIT_PASSWD, strlen(INIT_PASSWD))) {
00109 snprintf(errstr, errstr_len, "INIT_PASSWD does not match");
00110 goto err_open;
00111 }
00112 if(ctype<CONNECTION_TYPE_CLISERV)
00113 goto end;
00114 if(read_all(sock, &tmp64, sizeof(tmp64))<0) {
00115 snprintf(errstr, errstr_len, "Could not read cliserv_magic: %s",
00116 strerror(errno));
00117 goto err_open;
00118 }
00119 tmp64=ntohll(tmp64);
00120 if(tmp64 != cliserv_magic) {
00121 strncpy(errstr, "cliserv_magic does not match", errstr_len);
00122 goto err_open;
00123 }
00124 if(ctype<CONNECTION_TYPE_FULL)
00125 goto end;
00126
00127
00128
00129
00130
00131
00132
00133 read_all(sock, buf, sizeof(tmp64)+128);
00134 goto end;
00135 err_open:
00136 close(sock);
00137 err:
00138 sock=-1;
00139 end:
00140 return sock;
00141 }
00142
00143 int close_connection(int sock, CLOSE_TYPE type) {
00144 struct nbd_request req;
00145 u64 counter=0;
00146
00147 switch(type) {
00148 case CONNECTION_CLOSE_PROPERLY:
00149 req.magic=htonl(NBD_REQUEST_MAGIC);
00150 req.type=htonl(NBD_CMD_DISC);
00151 memcpy(&(req.handle), &(counter), sizeof(counter));
00152 counter++;
00153 req.from=0;
00154 req.len=0;
00155 if(write(sock, &req, sizeof(req))<0) {
00156 snprintf(errstr, errstr_len, "Could not write to socket: %s", strerror(errno));
00157 return -1;
00158 }
00159 case CONNECTION_CLOSE_FAST:
00160 if(close(sock)<0) {
00161 snprintf(errstr, errstr_len, "Could not close socket: %s", strerror(errno));
00162 return -1;
00163 }
00164 break;
00165 default:
00166 g_critical("Your compiler is on crack!");
00167 return -1;
00168 }
00169 return 0;
00170 }
00171
00172 int read_packet_check_header(int sock, size_t datasize, long long int curhandle) {
00173 struct nbd_reply rep;
00174 int retval=0;
00175 char buf[datasize];
00176
00177 read_all(sock, &rep, sizeof(rep));
00178 rep.magic=ntohl(rep.magic);
00179 rep.error=ntohl(rep.error);
00180 if(rep.magic!=NBD_REPLY_MAGIC) {
00181 snprintf(errstr, errstr_len, "Received package with incorrect reply_magic. Index of sent packages is %lld (0x%llX), received handle is %lld (0x%llX). Received magic 0x%lX, expected 0x%lX", curhandle, curhandle, *((u64*)rep.handle), *((u64*)rep.handle), rep.magic, NBD_REPLY_MAGIC);
00182 retval=-1;
00183 goto end;
00184 }
00185 if(rep.error) {
00186 snprintf(errstr, errstr_len, "Received error from server: %ld (0x%lX). Handle is %lld (0x%llX).", rep.error, *((u64*)rep.handle), *((u64*)rep.handle));
00187 retval=-1;
00188 goto end;
00189 }
00190 read_all(sock, &buf, datasize);
00191
00192 end:
00193 return retval;
00194 }
00195
00196 int throughput_test(gchar* hostname, int port, int sock, char sock_is_open, char close_sock) {
00197 long long int i;
00198 char buf[1024];
00199 struct nbd_request req;
00200 u64 size;
00201 int requests=0;
00202 fd_set set;
00203 struct timeval tv;
00204 struct timeval start;
00205 struct timeval stop;
00206 float timespan;
00207 int speed;
00208 char speedchar[2] = { '\0', '\0' };
00209 int retval=0;
00210 size_t tmp;
00211 signed int do_write=TRUE;
00212
00213 size=0;
00214 if(!sock_is_open) {
00215 if((sock=setup_connection(hostname, port, CONNECTION_TYPE_CLISERV))<0) {
00216 g_warning("Could not open socket: %s", errstr);
00217 retval=-1;
00218 goto err;
00219 }
00220 } else {
00221
00222
00223 size=4096;
00224 }
00225 if((tmp=read_all(sock, &size, sizeof(u64)))<0) {
00226 retval=-1;
00227 snprintf(errstr, errstr_len, "Could not read from socket: %s", strerror(errno));
00228 goto err_open;
00229 }
00230 if(tmp==0) {
00231 retval=-1;
00232 snprintf(errstr, errstr_len, "Server closed connection unexpectedly when trying to read size of device in throughput test");
00233 goto err;
00234 }
00235 read_all(sock,&buf,128);
00236 size=ntohll(size);
00237 req.magic=htonl(NBD_REQUEST_MAGIC);
00238 req.type=htonl(NBD_CMD_READ);
00239 req.len=htonl(1024);
00240 if(gettimeofday(&start, NULL)<0) {
00241 retval=-1;
00242 snprintf(errstr, errstr_len, "Could not measure start time: %s", strerror(errno));
00243 goto err_open;
00244 }
00245 for(i=0;i+1024<=size;i+=1024) {
00246 if(do_write) {
00247 *((u64*)req.handle)=i;
00248 req.from=htonll(i);
00249 write(sock, &req, sizeof(req));
00250 printf("Requests(+): %d\n", ++requests);
00251 }
00252 do {
00253 FD_ZERO(&set);
00254 FD_SET(sock, &set);
00255 tv.tv_sec=0;
00256 tv.tv_usec=0;
00257 select(sock+1, &set, NULL, NULL, &tv);
00258 if(FD_ISSET(sock, &set)) {
00259
00260
00261 if(read_packet_check_header(sock, 1024, i)<0) {
00262 retval=-1;
00263 goto err_open;
00264 }
00265 printf("Requests(-): %d\n", --requests);
00266 }
00267 } while FD_ISSET(sock, &set);
00268
00269
00270 FD_ZERO(&set);
00271 FD_SET(sock, &set);
00272 tv.tv_sec=1;
00273 tv.tv_usec=0;
00274 do_write=select(sock+1,NULL,&set,NULL,&tv);
00275 if(!do_write) printf("Select finished\n");
00276 if(do_write<0) {
00277 snprintf(errstr, errstr_len, "select: %s", strerror(errno));
00278 retval=-1;
00279 goto err_open;
00280 }
00281 }
00282
00283 do {
00284 FD_ZERO(&set);
00285 FD_SET(sock, &set);
00286 tv.tv_sec=0;
00287 tv.tv_usec=0;
00288 select(sock+1, &set, NULL, NULL, &tv);
00289 if(FD_ISSET(sock, &set)) {
00290
00291
00292 read_packet_check_header(sock, 1024, i);
00293 printf("Requests(-): %d\n", --requests);
00294 }
00295 } while (requests);
00296 if(gettimeofday(&stop, NULL)<0) {
00297 retval=-1;
00298 snprintf(errstr, errstr_len, "Could not measure end time: %s", strerror(errno));
00299 goto err_open;
00300 }
00301 timespan=stop.tv_sec-start.tv_sec+(stop.tv_usec-start.tv_usec)/1000000;
00302 speed=(int)(size/timespan);
00303 if(speed>1024) {
00304 speed>>=10;
00305 speedchar[0]='K';
00306 }
00307 if(speed>1024) {
00308 speed>>=10;
00309 speedchar[0]='M';
00310 }
00311 if(speed>1024) {
00312 speed>>=10;
00313 speedchar[0]='G';
00314 }
00315 g_message("Throughput test complete. Took %.3f seconds to complete, %d%sB/s",timespan,speed,speedchar);
00316
00317 err_open:
00318 if(close_sock) {
00319 close_connection(sock, CONNECTION_CLOSE_PROPERLY);
00320 }
00321 err:
00322 return retval;
00323 }
00324
00325 int main(int argc, char**argv) {
00326 gchar *hostname;
00327 long int p;
00328 int port;
00329 int sock;
00330
00331 if(argc<3) {
00332 g_message("Not enough arguments");
00333 g_message("Usage: %s <hostname> <port>", argv[0]);
00334 exit(EXIT_FAILURE);
00335 }
00336 logging();
00337 hostname=g_strdup(argv[1]);
00338 p=(strtol(argv[2], NULL, 0));
00339 if(p==LONG_MIN||p==LONG_MAX) {
00340 g_critical("Could not parse port number: %s", strerror(errno));
00341 exit(EXIT_FAILURE);
00342 }
00343 port=(int)p;
00344
00345 if(throughput_test(hostname, port, sock, FALSE, TRUE)<0) {
00346 g_warning("Could not run throughput test: %s", errstr);
00347 exit(EXIT_FAILURE);
00348 }
00349
00350 return 0;
00351 }