Changeset View
Changeset View
Standalone View
Standalone View
tests/sys/devad2/devad2_main.cc
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | |||||
* Copyright (c) 2013, 2014 Spectra Logic Corporation | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions, and the following disclaimer, | |||||
* without modification. | |||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer | |||||
* substantially similar to the "NO WARRANTY" disclaimer below | |||||
* ("Disclaimer") and any redistribution must be conditioned upon | |||||
* including a substantially similar Disclaimer requirement for further | |||||
* binary redistribution. | |||||
* | |||||
* NO WARRANTY | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR | |||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||||
* POSSIBILITY OF SUCH DAMAGES. | |||||
* | |||||
*/ | |||||
/* | |||||
* Spectra-specific inclusion of timespec*() | |||||
* Please see the mailing list thread regarding exposure: | |||||
* https://lists.freebsd.org/pipermail/svn-src-head/2013-February/045210.html | |||||
*/ | |||||
#define SPECTRA_TIMESPEC 1 | |||||
#include <pthread.h> | |||||
#include <stdint.h> | |||||
#include <stdio.h> | |||||
#include <stddef.h> | |||||
#include <stdlib.h> | |||||
#include <unistd.h> | |||||
#include <string.h> | |||||
#include <getopt.h> | |||||
#include <libgen.h> | |||||
#include <fcntl.h> | |||||
#include <signal.h> | |||||
#include <errno.h> | |||||
#include <err.h> | |||||
#include <sys/types.h> | |||||
#include <sys/stat.h> | |||||
#include <sys/queue.h> | |||||
#include <sys/sysctl.h> | |||||
#include <sys/event.h> | |||||
#include <sys/time.h> | |||||
#include <netinet/in.h> | |||||
#include <netinet/tcp_seq.h> | |||||
#include <machine/atomic.h> | |||||
#include <cam/cam.h> | |||||
#include <cam/cam_debug.h> | |||||
#include <cam/cam_ccb.h> | |||||
#include <cam/scsi/scsi_all.h> | |||||
#include <cam/scsi/scsi_da.h> | |||||
#include <cam/scsi/scsi_pass.h> | |||||
#include <cam/scsi/scsi_message.h> | |||||
#include <cam/scsi/scsi_da_brand.h> | |||||
#include <cam/scsi/smp_all.h> | |||||
#include <camlib.h> | |||||
#include <list> | |||||
#include <map> | |||||
#include <string> | |||||
#include <devdctl/guid.h> | |||||
#include <devdctl/event.h> | |||||
#include <devdctl/event_factory.h> | |||||
#include <devdctl/consumer.h> | |||||
#include "devad2.h" | |||||
#include "devad2_devctl.h" | |||||
#ifndef timespeccmp | |||||
#define timespeccmp(tvp, uvp, cmp) \ | |||||
(((tvp)->tv_sec == (uvp)->tv_sec) ? \ | |||||
((tvp)->tv_nsec cmp (uvp)->tv_nsec) : \ | |||||
((tvp)->tv_sec cmp (uvp)->tv_sec)) | |||||
#endif | |||||
#ifndef timespecadd | |||||
#define timespecadd(vvp, uvp) \ | |||||
do { \ | |||||
(vvp)->tv_sec += (uvp)->tv_sec; \ | |||||
(vvp)->tv_nsec += (uvp)->tv_nsec; \ | |||||
if ((vvp)->tv_nsec >= 1000000000) { \ | |||||
(vvp)->tv_sec++; \ | |||||
(vvp)->tv_nsec -= 1000000000; \ | |||||
} \ | |||||
} while (0) | |||||
#endif | |||||
#ifndef timespecsub | |||||
#define timespecsub(vvp, uvp) \ | |||||
do { \ | |||||
(vvp)->tv_sec -= (uvp)->tv_sec; \ | |||||
(vvp)->tv_nsec -= (uvp)->tv_nsec; \ | |||||
if ((vvp)->tv_nsec < 0) { \ | |||||
(vvp)->tv_sec--; \ | |||||
(vvp)->tv_nsec += 1000000000; \ | |||||
} \ | |||||
} while (0) | |||||
#endif | |||||
void * | |||||
devad_openclose_thread(void *arg) | |||||
{ | |||||
struct devad_openclose_args *args; | |||||
int retval; | |||||
args = (struct devad_openclose_args *)arg; | |||||
while ((args->flags & DEVAD_OC_FLAG_STOP) == 0) { | |||||
struct cam_device *dev; | |||||
#if 0 | |||||
fprintf(stdout, "opening %s\n", args->cam_device); | |||||
#endif | |||||
dev = cam_open_device(args->cam_device, O_RDWR); | |||||
if (dev == NULL) { | |||||
warnx("Can't open %s: %s", args->cam_device, | |||||
cam_errbuf); | |||||
goto bailout; | |||||
} | |||||
if (args->cycle_interval != 0) { | |||||
retval = sleep(args->cycle_interval); | |||||
if (retval != 0) { | |||||
warn("sleep returned an error"); | |||||
goto bailout; | |||||
} | |||||
} | |||||
#if 0 | |||||
fprintf(stdout, "closing %s\n", args->cam_device); | |||||
#endif | |||||
cam_close_device(dev); | |||||
} | |||||
bailout: | |||||
return (NULL); | |||||
} | |||||
int | |||||
devad_phy_cycle(struct devad_phy_args *args, int on) | |||||
{ | |||||
struct smp_phy_control_request *request = NULL; | |||||
struct smp_phy_control_response *response = NULL; | |||||
struct cam_device *dev = NULL; | |||||
int retval = 0; | |||||
union ccb *ccb = NULL; | |||||
dev = cam_open_device(args->enc_device, O_RDWR); | |||||
if (dev == NULL) { | |||||
warnx("Unable to open device %s: %s", args->enc_device, | |||||
cam_errbuf); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
fprintf(stdout, "Turning %s %d %s\n", args->enc_device, args->phy, | |||||
on ? "on" : "off"); | |||||
ccb = cam_getccb(dev); | |||||
if (ccb == NULL) { | |||||
warnx("%s: error allocating CCB to send to %s", | |||||
__func__, args->enc_device); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
bzero(&(&ccb->ccb_h)[1], | |||||
sizeof(union ccb) - sizeof(struct ccb_hdr)); | |||||
request = (struct smp_phy_control_request *)malloc(sizeof(*request)); | |||||
if (request == NULL) { | |||||
warn("Unable to allocate %zu bytes", sizeof(*request)); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
response = (struct smp_phy_control_response *)malloc(sizeof(*response)); | |||||
if (response == NULL) { | |||||
warn("Unable to allocate %zu bytes", sizeof(*response)); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
bzero(request, sizeof(*request)); | |||||
bzero(response, sizeof(*response)); | |||||
smp_phy_control(&ccb->smpio, | |||||
/*retries*/ 0, | |||||
/*cbfcnp*/ NULL, | |||||
/*request*/ request, | |||||
/*request_len*/ sizeof(*request), | |||||
/*response*/ (uint8_t *)response, | |||||
/*response_len*/ sizeof(*response), | |||||
/*long_response*/ 0, | |||||
/*expected_exp_change_count*/ 0, | |||||
/*phy*/ args->phy, | |||||
/*phy_operation*/ (on != 0) ? SMP_PC_PHY_OP_LINK_RESET : | |||||
SMP_PC_PHY_OP_DISABLE, | |||||
/*update_pp_timeout_val*/ 0, | |||||
/*attached_device_name*/ 0, | |||||
/*prog_min_prl*/ 0, | |||||
/*prog_max_prl*/ 0, | |||||
/*slumber_partial*/ 0, | |||||
/*pp_timeout_value*/ 0, | |||||
/*timeout*/ 5000); | |||||
retval = cam_send_ccb(dev, ccb); | |||||
if (retval != 0) { | |||||
warn("error sending SMP phy control command to %s", | |||||
args->enc_device); | |||||
} | |||||
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | |||||
cam_error_print(dev, ccb, CAM_ESF_ALL, CAM_EPF_NORMAL, stderr); | |||||
retval = 1; | |||||
} | |||||
bailout: | |||||
free(request); | |||||
free(response); | |||||
if (ccb != NULL) | |||||
cam_freeccb(ccb); | |||||
if (dev != NULL) | |||||
cam_close_device(dev); | |||||
return (retval); | |||||
} | |||||
void * | |||||
devad_phy_thread(void *arg) | |||||
{ | |||||
struct devad_phy_args *args; | |||||
uint32_t startup_sleep; | |||||
int retval; | |||||
args = (struct devad_phy_args *)arg; | |||||
startup_sleep = arc4random() % 10; | |||||
retval = sleep(startup_sleep); | |||||
while ((args->flags & DEVAD_PHY_FLAG_STOP) == 0) { | |||||
retval = devad_phy_cycle(args, /*on*/ 0); | |||||
if (retval != 0) | |||||
goto bailout; | |||||
retval = sleep(args->cycle_interval); | |||||
if (retval != 0) { | |||||
warnx("%s: got signal while waiting", args->enc_device); | |||||
goto bailout; | |||||
} | |||||
retval = devad_phy_cycle(args, /*on*/ 1); | |||||
if (retval != 0) | |||||
goto bailout; | |||||
retval = sleep(args->cycle_interval); | |||||
if (retval != 0) { | |||||
warnx("%s: got signal while waiting", args->enc_device); | |||||
goto bailout; | |||||
} | |||||
} | |||||
bailout: | |||||
return (NULL); | |||||
} | |||||
void | |||||
usage(void) | |||||
{ | |||||
printf("devad <-p enc,phy> [-t secs] [-i sleep_interval] [-o dev] " | |||||
"[-I openclose_interval]\n"); | |||||
} | |||||
int | |||||
main(int argc, char **argv) | |||||
{ | |||||
struct devad_softc *softc; | |||||
struct devad_phy_args *phy_arg; | |||||
struct devad_openclose_args *openclose_arg; | |||||
struct devad_disk *disk; | |||||
struct kevent ke[10], ke_set; | |||||
struct timespec end_time; | |||||
uint64_t res_key = 0; | |||||
int num_phys = 0, num_openclose = 0, num_events; | |||||
int sleep_interval = 5; | |||||
int openclose_interval = 0; | |||||
int timed_run = 0; | |||||
int retval; | |||||
int i, c; | |||||
softc = (struct devad_softc *)malloc(sizeof(*softc)); | |||||
if (softc == NULL) | |||||
err(1, "Unable to allocate %zu bytes", sizeof(*softc)); | |||||
bzero(softc, sizeof(*softc)); | |||||
TAILQ_INIT(&softc->disk_list); | |||||
STAILQ_INIT(&softc->phy_list); | |||||
STAILQ_INIT(&softc->openclose_list); | |||||
pthread_mutex_init(&softc->mutex, NULL); | |||||
while ((c = getopt(argc, argv, "i:I:o:p:t:")) != -1) { | |||||
switch (c) { | |||||
case 'i': | |||||
case 'I': { | |||||
char *endptr; | |||||
int tmp_interval; | |||||
tmp_interval = strtol(optarg, &endptr, 0); | |||||
if (*endptr != '\0') | |||||
errx(1, "Invalid sleep interval %s", optarg); | |||||
else if (tmp_interval < 0) | |||||
errx(1, "Negative sleep interval %d not valid", | |||||
tmp_interval); | |||||
if (c == 'i') | |||||
sleep_interval = tmp_interval; | |||||
else | |||||
openclose_interval = tmp_interval; | |||||
break; | |||||
} | |||||
case 'o': { | |||||
char *tmpstr; | |||||
openclose_arg = (struct devad_openclose_args *) | |||||
malloc(sizeof(*openclose_arg)); | |||||
if (openclose_arg == NULL) | |||||
err(1, "Unable to malloc %zu bytes", | |||||
sizeof(*openclose_arg)); | |||||
bzero(openclose_arg, sizeof(*openclose_arg)); | |||||
strlcpy(openclose_arg->cam_device, optarg, | |||||
sizeof(openclose_arg->cam_device)); | |||||
STAILQ_INSERT_TAIL(&softc->openclose_list, | |||||
openclose_arg, links); | |||||
num_openclose++; | |||||
break; | |||||
} | |||||
case 'p': { | |||||
char *tmpstr, *tmpstr2, *endptr; | |||||
phy_arg = (struct devad_phy_args *)malloc( | |||||
sizeof(*phy_arg)); | |||||
if (phy_arg == NULL) | |||||
err(1, "Unable to malloc %zu bytes", | |||||
sizeof(*phy_arg)); | |||||
bzero(phy_arg, sizeof(*phy_arg)); | |||||
tmpstr = strdup(optarg); | |||||
if (tmpstr == NULL) | |||||
err(1, "Unable to allocate PHY argument " | |||||
"storage"); | |||||
tmpstr2 = strsep(&tmpstr, ","); | |||||
if ((tmpstr2 == NULL) || (*tmpstr2 == '\0')) | |||||
errx(1, "Invalid PHY argument %s", optarg); | |||||
strlcpy(phy_arg->enc_device, tmpstr2, | |||||
sizeof(phy_arg->enc_device)); | |||||
tmpstr2 = strsep(&tmpstr, ","); | |||||
if ((tmpstr2 == NULL) || (*tmpstr2 == '\0')) | |||||
errx(1, "Invalid PHY argument %s", optarg); | |||||
phy_arg->phy = strtol(tmpstr2, &endptr, 0); | |||||
if (*endptr != '\0') | |||||
errx(1, "Invalid PHY number %s", tmpstr2); | |||||
else if (phy_arg->phy < 0) | |||||
errx(1, "Invalid PHY number %d", phy_arg->phy); | |||||
STAILQ_INSERT_TAIL(&softc->phy_list, phy_arg, links); | |||||
num_phys++; | |||||
break; | |||||
} | |||||
case 't': { | |||||
unsigned long execute_secs; | |||||
struct timespec add_time; | |||||
char *endptr; | |||||
execute_secs = strtoul(optarg, &endptr, 0); | |||||
if (*endptr != '\0') | |||||
errx(1, "Invalid time limit %s", optarg); | |||||
retval = clock_gettime(CLOCK_MONOTONIC_FAST, &end_time); | |||||
if (retval != 0) | |||||
err(1, "Unable to get current time"); | |||||
add_time.tv_sec = execute_secs; | |||||
add_time.tv_nsec = 0; | |||||
timespecadd(&end_time, &add_time); | |||||
timed_run = 1; | |||||
break; | |||||
} | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
if (num_phys == 0) { | |||||
warnx("you must specify at least one PHY"); | |||||
usage(); | |||||
exit (1); | |||||
} | |||||
softc->res_key = res_key; | |||||
softc->kq = kqueue(); | |||||
if (softc->kq == -1) | |||||
err(1, "Unable to create kqueue"); | |||||
/* | |||||
* Initial disk scan. Figure out what disks are here before we | |||||
* start cycling PHYs. | |||||
*/ | |||||
retval = devad_disk_scan(softc, /*print_devs*/ 0); | |||||
if (retval != 0) | |||||
errx(1, "Initial disk scan failed"); | |||||
/* | |||||
* Register for devd events. | |||||
*/ | |||||
EventConsumer::Init(softc); | |||||
if (!EventConsumer::Get().Connected()) | |||||
EventConsumer::Get().ConnectToDevd(); | |||||
if (!EventConsumer::Get().Connected()) | |||||
errx(1, "Not connected to devd, cannot run test"); | |||||
bzero(&ke_set, sizeof(&ke_set)); | |||||
EV_SET(&ke_set, EventConsumer::Get().GetPollFd(), | |||||
EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); | |||||
if (kevent(softc->kq, &ke_set, 1, NULL, 0, NULL) == -1) | |||||
err(1, "error registering kevent"); | |||||
/* | |||||
* Start the PHY cycling threads. | |||||
*/ | |||||
STAILQ_FOREACH(phy_arg, &softc->phy_list, links) { | |||||
phy_arg->cycle_interval = sleep_interval; | |||||
retval = pthread_create(&phy_arg->thread_data, NULL, | |||||
devad_phy_thread, phy_arg); | |||||
if (retval != 0) | |||||
err(1, "Unable to create PHY cycle thread"); | |||||
} | |||||
/* | |||||
* Start the open/close threads. | |||||
*/ | |||||
STAILQ_FOREACH(openclose_arg, &softc->openclose_list, links) { | |||||
openclose_arg->cycle_interval = openclose_interval; | |||||
retval = pthread_create(&openclose_arg->thread_data, NULL, | |||||
devad_openclose_thread, | |||||
openclose_arg); | |||||
if (retval != 0) | |||||
err(1, "Unable to create open/close thread"); | |||||
} | |||||
/* | |||||
* Look for events and process them. | |||||
*/ | |||||
for (;;) { | |||||
if (timed_run != 0) { | |||||
struct timespec cur_time; | |||||
retval = clock_gettime(CLOCK_MONOTONIC_FAST, &cur_time); | |||||
if (retval != 0) | |||||
err(1, "clock_gettime(2) failed"); | |||||
if (timespeccmp(&cur_time, &end_time, >)) | |||||
break; | |||||
} | |||||
num_events = kevent(softc->kq, NULL, 0, ke, | |||||
sizeof(ke) / sizeof(ke[0]), NULL); | |||||
if (num_events == -1) | |||||
err(1, "Error returned from kevent"); | |||||
for (i = 0; i < num_events; i++) { | |||||
if (ke[i].ident == EventConsumer::Get().GetPollFd()) | |||||
EventConsumer::Get().ProcessEvents(); | |||||
else { | |||||
struct devad_disk *disk, *disk2; | |||||
disk = (struct devad_disk *)ke[i].udata; | |||||
devad_disk_read_done(disk); | |||||
} | |||||
} | |||||
} | |||||
/* | |||||
* Cancel the PHY cycling threads. | |||||
*/ | |||||
STAILQ_FOREACH(phy_arg, &softc->phy_list, links) { | |||||
#if 0 | |||||
retval = pthread_cancel(phy_arg->thread_data); | |||||
if (retval != 0) | |||||
warn("Unable to cancel thread for PHY %d on %s", | |||||
phy_arg->phy, phy_arg->enc_device); | |||||
#endif | |||||
phy_arg->flags |= DEVAD_PHY_FLAG_STOP; | |||||
} | |||||
/* | |||||
* Cancel the open/close threads. | |||||
*/ | |||||
STAILQ_FOREACH(openclose_arg, &softc->openclose_list, links) { | |||||
#if 0 | |||||
retval = pthread_cancel(openclose_arg->thread_data); | |||||
if (retval != 0) | |||||
warn("Unable to cancel open/close thread for %s", | |||||
openclose_arg->cam_device); | |||||
#endif | |||||
openclose_arg->flags |= DEVAD_OC_FLAG_STOP; | |||||
} | |||||
/* | |||||
* Wait for the PHY cycling threads to exit. | |||||
*/ | |||||
STAILQ_FOREACH(phy_arg, &softc->phy_list, links) { | |||||
retval = pthread_join(phy_arg->thread_data, NULL); | |||||
if (retval != 0) | |||||
warn("Unable to join thread for PHY %d on %s", | |||||
phy_arg->phy, phy_arg->enc_device); | |||||
} | |||||
/* | |||||
* Wait for the open/close threads to exit. | |||||
*/ | |||||
STAILQ_FOREACH(openclose_arg, &softc->openclose_list, links) { | |||||
retval = pthread_join(openclose_arg->thread_data, NULL); | |||||
if (retval != 0) | |||||
warn("Unable to join open/close thread for %s", | |||||
openclose_arg->cam_device); | |||||
} | |||||
for (disk = TAILQ_FIRST(&softc->disk_list); disk != NULL; | |||||
disk = TAILQ_FIRST(&softc->disk_list)) { | |||||
devad_disk_remove(softc, disk); | |||||
} | |||||
for (phy_arg = STAILQ_FIRST(&softc->phy_list); phy_arg != NULL; | |||||
phy_arg = STAILQ_FIRST(&softc->phy_list)) { | |||||
STAILQ_REMOVE_HEAD(&softc->phy_list, links); | |||||
free(phy_arg); | |||||
} | |||||
for (openclose_arg = STAILQ_FIRST(&softc->openclose_list); | |||||
openclose_arg != NULL; | |||||
openclose_arg = STAILQ_FIRST(&softc->openclose_list)) { | |||||
STAILQ_REMOVE_HEAD(&softc->openclose_list, links); | |||||
free(openclose_arg); | |||||
} | |||||
exit (0); | |||||
} |