static int set_if_addr(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res; unsigned char *ipaddr; int i; struct { char *req_name; char *desc; int g_ioctl; int s_ioctl; } ifra[] = { {"IFADDR", "addr", SIOCGIFADDR, SIOCSIFADDR}, {"DSTADDR", "destination addr", SIOCGIFDSTADDR, SIOCSIFDSTADDR}, {"BRDADDR", "broadcast addr", SIOCGIFBRDADDR, SIOCSIFBRDADDR}, {"NETMASK", "netmask", SIOCGIFNETMASK, SIOCSIFNETMASK}, {NULL, NULL, 0, 0}, }; for (i = 0; ifra[i].req_name; i++) { strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); res = ioctl(skfd, ifra[i].g_ioctl, &ifr); if (res < 0) { int saved_errno = errno; v_print("Interface '%s': Error: SIOCG%s failed: %s\n", master_ifname, ifra[i].req_name, strerror(saved_errno)); ifr.ifr_addr.sa_family = AF_INET; memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); } strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); res = ioctl(skfd, ifra[i].s_ioctl, &ifr); if (res < 0) { int saved_errno = errno; v_print("Interface '%s': Error: SIOCS%s failed: %s\n", slave_ifname, ifra[i].req_name, strerror(saved_errno)); } ipaddr = (unsigned char *)ifr.ifr_addr.sa_data; v_print("Interface '%s': set IP %s to %d.%d.%d.%d\n", slave_ifname, ifra[i].desc, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); } return 0; }
static int change_active(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res = 0; if (!(slave_flags.ifr_flags & IFF_SLAVE)) { fprintf(stderr, "Illegal operation: The specified slave interface " "'%s' is not a slave\n", slave_ifname); return 1; } strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); if ((ioctl(skfd, SIOCBONDCHANGEACTIVE, &ifr) < 0) && (ioctl(skfd, BOND_CHANGE_ACTIVE_OLD, &ifr) < 0)) { saved_errno = errno; v_print("Master '%s': Error: SIOCBONDCHANGEACTIVE failed: " "%s\n", master_ifname, strerror(saved_errno)); res = 1; } return res; }
static int get_slave_flags(char *slave_ifname) { int res = 0; strncpy(slave_flags.ifr_name, slave_ifname, IFNAMSIZ); res = ioctl(skfd, SIOCGIFFLAGS, &slave_flags); if (res < 0) { saved_errno = errno; v_print("Slave '%s': Error: SIOCGIFFLAGS failed: %s\n", slave_ifname, strerror(saved_errno)); } else { v_print("Slave %s: flags %04X.\n", slave_ifname, slave_flags.ifr_flags); } return res; }
/*! @brief Print a Vars object to the terminal. @ingroup print_high @param ptr Pointer to Vars object. */ void v_show(void *ptr) { static FILE *tty = NULL; if (tty == NULL) tty = fopen("/dev/tty", "w"); v_print(ptr, tty); }
int main(int argc, char** argv) { Vector* v = (Vector*) malloc(sizeof(Vector)); v_init(v, 0); int i; printf("[test-1] push 10 closures to bottom\n"); for (i = 0; i < 10; i++) { Closure* c = (Closure*) malloc(sizeof(Closure)); cl_init(c); c->level = i; v_push_closure(v, c); } v_print(v); printf("[test-2] push 10 closures next to their roots\n"); for (i = 0; i < 10; i++) { Closure* c = (Closure*) malloc(sizeof(Closure)); cl_init(c); c->level = i; v_push_closure(v, c); } v_print(v); printf("[test-3] pop 10 closures from top\n"); for (i = 0; i < 10; i++) { Closure* c = v_pop_top(v); cl_free(c); } v_print(v); printf("[test-4] pop 10 closures from bot\n"); for (i = 0; i < 10; i++) { Closure* c = v_pop_bottom(v); cl_free(c); } v_print(v); v_free(v); return 0; }
static int set_slave_mtu(char *slave_ifname, int mtu) { struct ifreq ifr; int res = 0; ifr.ifr_mtu = mtu; strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); res = ioctl(skfd, SIOCSIFMTU, &ifr); if (res < 0) { saved_errno = errno; v_print("Slave '%s': Error: SIOCSIFMTU failed: %s\n", slave_ifname, strerror(saved_errno)); } else { v_print("Slave '%s': MTU set to %d.\n", slave_ifname, mtu); } return res; }
static int set_if_flags(char *ifname, short flags) { struct ifreq ifr; int res = 0; ifr.ifr_flags = flags; strncpy(ifr.ifr_name, ifname, IFNAMSIZ); res = ioctl(skfd, SIOCSIFFLAGS, &ifr); if (res < 0) { saved_errno = errno; v_print("Interface '%s': Error: SIOCSIFFLAGS failed: %s\n", ifname, strerror(saved_errno)); } else { v_print("Interface '%s': flags set to %04X.\n", ifname, flags); } return res; }
static int clear_if_addr(char *ifname) { struct ifreq ifr; int res = 0; strncpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_addr.sa_family = AF_INET; memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); res = ioctl(skfd, SIOCSIFADDR, &ifr); if (res < 0) { saved_errno = errno; v_print("Interface '%s': Error: SIOCSIFADDR failed: %s\n", ifname, strerror(saved_errno)); } else { v_print("Interface '%s': address cleared\n", ifname); } return res; }
static int get_drv_info(char *master_ifname) { struct ifreq ifr; struct ethtool_drvinfo info; char *endptr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); ifr.ifr_data = (caddr_t)&info; info.cmd = ETHTOOL_GDRVINFO; strncpy(info.driver, "ifenslave", 32); snprintf(info.fw_version, 32, "%d", BOND_ABI_VERSION); if (ioctl(skfd, SIOCETHTOOL, &ifr) < 0) { if (errno == EOPNOTSUPP) { goto out; } saved_errno = errno; v_print("Master '%s': Error: get bonding info failed %s\n", master_ifname, strerror(saved_errno)); return 1; } abi_ver = strtoul(info.fw_version, &endptr, 0); if (*endptr) { v_print("Master '%s': Error: got invalid string as an ABI " "version from the bonding module\n", master_ifname); return 1; } out: v_print("ABI ver is %d\n", abi_ver); return 0; }
static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr) { unsigned char *addr = (unsigned char *)hwaddr->sa_data; struct ifreq ifr; int res = 0; strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); res = ioctl(skfd, SIOCSIFHWADDR, &ifr); if (res < 0) { saved_errno = errno; v_print("Master '%s': Error: SIOCSIFHWADDR failed: %s\n", master_ifname, strerror(saved_errno)); return res; } else { v_print("Master '%s': hardware address set to " "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", master_ifname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); } return res; }
static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr) { unsigned char *addr = (unsigned char *)hwaddr->sa_data; struct ifreq ifr; int res = 0; strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); res = ioctl(skfd, SIOCSIFHWADDR, &ifr); if (res < 0) { saved_errno = errno; v_print("Slave '%s': Error: SIOCSIFHWADDR failed: %s\n", slave_ifname, strerror(saved_errno)); if (saved_errno == EBUSY) { v_print(" The device is busy: it must be idle " "before running this command.\n"); } else if (saved_errno == EOPNOTSUPP) { v_print(" The device does not support setting " "the MAC address.\n" " Your kernel likely does not support slave " "devices.\n"); } else if (saved_errno == EINVAL) { v_print(" The device's address type does not match " "the master's address type.\n"); } return res; } else { v_print("Slave '%s': hardware address set to " "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", slave_ifname, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); } return res; }
static int get_if_settings(char *ifname, struct dev_ifr ifra[]) { int i; int res = 0; for (i = 0; ifra[i].req_ifr; i++) { strncpy(ifra[i].req_ifr->ifr_name, ifname, IFNAMSIZ); res = ioctl(skfd, ifra[i].req_type, ifra[i].req_ifr); if (res < 0) { saved_errno = errno; v_print("Interface '%s': Error: %s failed: %s\n", ifname, ifra[i].req_name, strerror(saved_errno)); return saved_errno; } } return 0; }
/* Print contents of a queue */ void vq_print(vqueue *q, FILE *fp) { int i; VQ_CHECK(q); v_print_start(); v_push_indent(); v_print_type(vqueue_type, q, fp); for (i = 1; i < q->entries; i++) { v_indent(fp); fprintf(fp, "PRIORITY %g => ", QVAL(q, i)->priority); v_print(QVAL(q, i)->val, fp); } v_pop_indent(); v_print_finish(); }
static int release(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res = 0; if (!(slave_flags.ifr_flags & IFF_SLAVE)) { fprintf(stderr, "Illegal operation: The specified slave interface " "'%s' is not a slave\n", slave_ifname); return 1; } strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); if ((ioctl(skfd, SIOCBONDRELEASE, &ifr) < 0) && (ioctl(skfd, BOND_RELEASE_OLD, &ifr) < 0)) { saved_errno = errno; v_print("Master '%s': Error: SIOCBONDRELEASE failed: %s\n", master_ifname, strerror(saved_errno)); return 1; } else if (abi_ver < 1) { /* The driver is using an old ABI, so we'll set the interface * down to avoid any conflicts due to same MAC/IP */ res = set_if_down(slave_ifname, slave_flags.ifr_flags); if (res) { fprintf(stderr, "Slave '%s': Error: bring interface " "down failed\n", slave_ifname); } } /* set to default mtu */ set_slave_mtu(slave_ifname, 1500); return res; }
static int release(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res = 0; if (!(slave_flags.ifr_flags & IFF_SLAVE)) { fprintf(stderr, "Illegal operation: The specified slave interface " "'%s' is not a slave\n", slave_ifname); return 1; } strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); if ((ioctl(skfd, SIOCBONDRELEASE, &ifr) < 0) && (ioctl(skfd, BOND_RELEASE_OLD, &ifr) < 0)) { saved_errno = errno; v_print("Master '%s': Error: SIOCBONDRELEASE failed: %s\n", master_ifname, strerror(saved_errno)); return 1; } else if (abi_ver < 1) { res = set_if_down(slave_ifname, slave_flags.ifr_flags); if (res) { fprintf(stderr, "Slave '%s': Error: bring interface " "down failed\n", slave_ifname); } } set_slave_mtu(slave_ifname, 1500); return res; }
int main(int argc, char *argv[]) { char **spp, *master_ifname, *slave_ifname; int c, i, rv; int res = 0; int exclusive = 0; while ((c = getopt_long(argc, argv, "acdfhuvV", longopts, 0)) != EOF) { switch (c) { case 'a': opt_a++; exclusive++; break; case 'c': opt_c++; exclusive++; break; case 'd': opt_d++; exclusive++; break; case 'f': opt_f++; exclusive++; break; case 'h': opt_h++; exclusive++; break; case 'u': opt_u++; exclusive++; break; case 'v': opt_v++; break; case 'V': opt_V++; exclusive++; break; case '?': fprintf(stderr, usage_msg); res = 2; goto out; } } /* options check */ if (exclusive > 1) { fprintf(stderr, usage_msg); res = 2; goto out; } if (opt_v || opt_V) { printf(version); if (opt_V) { res = 0; goto out; } } if (opt_u) { printf(usage_msg); res = 0; goto out; } if (opt_h) { printf(usage_msg); printf(help_msg); res = 0; goto out; } /* Open a basic socket */ if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); res = 1; goto out; } if (opt_a) { if (optind == argc) { /* No remaining args */ /* show all interfaces */ if_print((char *)NULL); goto out; } else { /* Just show usage */ fprintf(stderr, usage_msg); res = 2; goto out; } } /* Copy the interface name */ spp = argv + optind; master_ifname = *spp++; if (master_ifname == NULL) { fprintf(stderr, usage_msg); res = 2; goto out; } /* exchange abi version with bonding module */ res = get_drv_info(master_ifname); if (res) { fprintf(stderr, "Master '%s': Error: handshake with driver failed. " "Aborting\n", master_ifname); goto out; } slave_ifname = *spp++; if (slave_ifname == NULL) { if (opt_d || opt_c) { fprintf(stderr, usage_msg); res = 2; goto out; } /* A single arg means show the * configuration for this interface */ if_print(master_ifname); goto out; } res = get_if_settings(master_ifname, master_ifra); if (res) { /* Probably a good reason not to go on */ fprintf(stderr, "Master '%s': Error: get settings failed: %s. " "Aborting\n", master_ifname, strerror(res)); goto out; } /* check if master is indeed a master; * if not then fail any operation */ if (!(master_flags.ifr_flags & IFF_MASTER)) { fprintf(stderr, "Illegal operation; the specified interface '%s' " "is not a master. Aborting\n", master_ifname); res = 1; goto out; } /* check if master is up; if not then fail any operation */ if (!(master_flags.ifr_flags & IFF_UP)) { fprintf(stderr, "Illegal operation; the specified master interface " "'%s' is not up.\n", master_ifname); res = 1; goto out; } /* Only for enslaving */ if (!opt_c && !opt_d) { sa_family_t master_family = master_hwaddr.ifr_hwaddr.sa_family; unsigned char *hwaddr = (unsigned char *)master_hwaddr.ifr_hwaddr.sa_data; /* The family '1' is ARPHRD_ETHER for ethernet. */ if (master_family != 1 && !opt_f) { fprintf(stderr, "Illegal operation: The specified master " "interface '%s' is not ethernet-like.\n " "This program is designed to work with " "ethernet-like network interfaces.\n " "Use the '-f' option to force the " "operation.\n", master_ifname); res = 1; goto out; } /* Check master's hw addr */ for (i = 0; i < 6; i++) { if (hwaddr[i] != 0) { hwaddr_set = 1; break; } } if (hwaddr_set) { v_print("current hardware address of master '%s' " "is %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " "type %d\n", master_ifname, hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5], master_family); } } /* Accepts only one slave */ if (opt_c) { /* change active slave */ res = get_slave_flags(slave_ifname); if (res) { fprintf(stderr, "Slave '%s': Error: get flags failed. " "Aborting\n", slave_ifname); goto out; } res = change_active(master_ifname, slave_ifname); if (res) { fprintf(stderr, "Master '%s', Slave '%s': Error: " "Change active failed\n", master_ifname, slave_ifname); } } else { /* Accept multiple slaves */ do { if (opt_d) { /* detach a slave interface from the master */ rv = get_slave_flags(slave_ifname); if (rv) { /* Can't work with this slave. */ /* remember the error and skip it*/ fprintf(stderr, "Slave '%s': Error: get flags " "failed. Skipping\n", slave_ifname); res = rv; continue; } rv = release(master_ifname, slave_ifname); if (rv) { fprintf(stderr, "Master '%s', Slave '%s': Error: " "Release failed\n", master_ifname, slave_ifname); res = rv; } } else { /* attach a slave interface to the master */ rv = get_if_settings(slave_ifname, slave_ifra); if (rv) { /* Can't work with this slave. */ /* remember the error and skip it*/ fprintf(stderr, "Slave '%s': Error: get " "settings failed: %s. " "Skipping\n", slave_ifname, strerror(rv)); res = rv; continue; } rv = enslave(master_ifname, slave_ifname); if (rv) { fprintf(stderr, "Master '%s', Slave '%s': Error: " "Enslave failed\n", master_ifname, slave_ifname); res = rv; } } } while ((slave_ifname = *spp++) != NULL); } out: if (skfd >= 0) { close(skfd); } return res; }
/* Ideally, vector_append would be consistent, durable, and _atomic_, which * would mean that it doesn't have to be _recovered_ after a failure. This * is probably possible by following the instructions in "A Lock-Free * Dynamically Resizable Array." However, this would require a significant * restructuring of the vector code that we already have, so we won't do * this for now. Instead, when the vector needs resizing, we focus on making * it _recoverable_, rather than atomic; when the vector doesn't need resizing, * then append is consistent and durable and _repeatable_, rather than atomic. * * Note that vector_append is NOT thread-safe or "lock-free" - high-level * synchronization is needed so that only one append occurs at a time! * * Relevant links: * http://www2.research.att.com/~bs/lock-free-vector.pdf * https://parasol.tamu.edu/~peterp/slides/opodis06.pdf * Intel patent, 8006064: "Lock-free vector utilizing a resource allocator...": * http://www.google.com/patents/US8006064?printsec=abstract#v=onepage&q&f=false * http://www.ibm.com/developerworks/aix/library/au-intelthreadbuilding/index.html?ca=drs- * http://software.intel.com/en-us/blogs/2009/04/09/delusion-of-tbbconcurrent_vectors-size-or-3-ways-to-traverse-in-parallel-correctly/ * * This function allows NULL pointers for the element put into the array, * for now. * * NOTE: for version 3.1 (simultaneous merges with conflict detection), I * hacked this interface to return the element that used to be the last * element in the vector, before we appended this one. Hmmm... */ int vector_append(vector *v, const void *e, void **previous_tail) { unsigned long long orig_size, new_size; int flush_size = 0; /* HEADS UP: this function is mostly the same as vector_insert(), so * if you update one, you should probably update the other! */ if (v == NULL) { v_error("v is NULL\n"); return -1; } if (v->count == VECTOR_MAX_COUNT) { v_error("hit maximum vector length: v->count=%llu\n", v->count); return -1; } #ifndef UNSAFE_COMMIT //#ifdef VECTOR_ASSERT #if 0 /* This assert only makes sense if we're not garbage collecting or * otherwise removing things from vectors. Note that the resizing * code doesn't contain any explicit checks for this (it will * happily reduce the size of the vector below the initial size * if you remove enough things); maybe this behavior should change, * but whatever. */ if (v->size < VECTOR_INIT_SIZE) { v_die("vector size %llu is less than initial size %d!!\n", v->size, VECTOR_INIT_SIZE); } #endif #endif /* When the last array slot is exhausted, increase the size of the * array by multiplying it by the resize factor. * Resizing the array consists of two steps: A) re-allocing the memory * region, and B) setting the new size of the vector. A) is repeatable, * but unfortunately could result in a memory leak if we don't correctly * remember the pointer AND remember the new size! To minimize the leak * window here, we immediately flush both the pointer and the size (which * should be adjacent to each other in the struct!) after the realloc. * We calculate the size of the flush that we need to perform by using * the offsetof operator, because the compiler may insert padding between * members that we don't know about. This code assumes that the order of * the members in the struct is [data, size, count]. * * ... */ if (v->size == v->count) { #ifdef VECTOR_RESIZE_PRINT v_print("v->size hit v->count = %llu; append resizing!!!\n", v->size); #endif v_debug("v->size hit v->count = %llu; resizing!!!\n", v->size); /* Check if multiplying v->size by VECTOR_RESIZE_FACTOR will put * it over the VECTOR_MAX_COUNT: */ if (v->size > (VECTOR_MAX_COUNT / VECTOR_RESIZE_FACTOR)) { v_debug("vector size (%llu) times resize factor (%d) would " "overflow max count (%llu), so setting size directly " "to max count\n", v->size, VECTOR_RESIZE_FACTOR, VECTOR_MAX_COUNT); new_size = VECTOR_MAX_COUNT; } else { new_size = v->size * VECTOR_RESIZE_FACTOR; } #ifdef VECTOR_RESIZE_PRINT v_print("calculated new_size=%llu (append)\n", new_size); #endif orig_size = v->size; #ifdef UNSAFE_COMMIT if (new_size > UNSAFE_COMMIT_LOG_SIZE) { v_print("WARNING: new vector size is greater than %d, probably " "means that we're resizing the commit_log vector!!\n", UNSAFE_COMMIT_LOG_SIZE); } #endif #ifdef V_ASSERT if (new_size > 100) { v_debug("WARNING: resizing vector to new_size=%llu\n", new_size); } #endif /* We expect the flush_size to be 12, but the compiler could possibly * insert padding that changes this. On brief examination, no padding * is inserted and both the data pointer and the size are flushed in * a single flush. * todo: could put the following code segment in its own "pcm_realloc()" * function... */ if (v->use_nvm) { /* We only need to flush data and size; count comes _after_ size, * so use it as the end of the range to flush. */ flush_size = offsetof(vector, count) - offsetof(vector, data); #ifdef VECTOR_ASSERT if (flush_size < (sizeof(void **) + sizeof(unsigned long long))) { v_die("got unexpected flush_size %d! offsetof(count)=%zu, " "offsetof(data)=%zu\n", flush_size, offsetof(vector, count), offsetof(vector, data)); } //v_print("calculated flush_size %d from offsetof(count)=%u, " // "offsetof(data)=%d\n", flush_size, // offsetof(vector, count), offsetof(vector, data)); #endif } /* Leak window begin. Note that kp_realloc() doesn't flush internally, * because we want to flush both the data and the size in the same * cache line to get consistency * Also note that we don't currently GUARANTEE this, if the compiler * happens to allocate v->data and v->size in two different cache * lines. */ kp_realloc((void **)&(v->data), sizeof(void*) * new_size, v->use_nvm); v->size = new_size; kp_flush_range(&(v->data), flush_size, v->use_nvm); //flush both data and size! /* Leak window end. If we fail after the flush() has returned, * then the next call to vector_append() will skip the resizing * step. */ if (v->data == NULL) { v_error("kp_realloc(array) failed\n"); /* Best effort on failure: reset size to what it was before, * and let caller handle the rest. */ v->size = orig_size; return -1; } v_debug("re-allocated array, now has size %llu (%llu slots)\n", sizeof(void*) * v->size, v->size); } /* The actual append part of vector_append() is repeatable: we first * fill in the element in the data array, then increment the count. * If we fail in-between these two steps, then vector_append() can * just be called again and we'll overwrite the memory area with the * same value. We do, however, have to flush the written element before * incrementing the count: we don't want the incremented count to hit * memory before the new element does. * ACTUALLY, this makes the vector_append ATOMIC, not repeatable (right?). * After a failure, if the caller didn't get a return value from this * function, then it can't be certain whether or not the append succeeded, * and so it should probably do a vector_get() to check if the append * happened or not. */ if (previous_tail) { //super hacky if (v->count > 0) { *previous_tail = v->data[v->count - 1]; } else { *previous_tail = NULL; } /* Don't need to flush; caller will do it... */ } /* Use two flushes here to make this "repeatable" - if we fail after * the first set + flush, there are no real effects. */ v->data[v->count] = (void *)e; kp_flush_range(&(v->data[v->count]), sizeof(void *), v->use_nvm); /* Do we need a memory fence right here? Only if we're flushing (so * the fence is already internal in kp_flush_range()); otherwise, * we're not concerned about anybody else seeing the count and the * element out-of-order... (right?). */ v->count++; kp_flush_range((void *)&(v->count), sizeof(unsigned long long), v->use_nvm); v_debug("stored new element %s in slot %llu (now count=%llu, size=%llu)\n", (char *)(v->data[(v->count)-1]), v->count-1, v->count, v->size); return 0; }
static int enslave(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res = 0; if (slave_flags.ifr_flags & IFF_SLAVE) { fprintf(stderr, "Illegal operation: The specified slave interface " "'%s' is already a slave\n", slave_ifname); return 1; } res = set_if_down(slave_ifname, slave_flags.ifr_flags); if (res) { fprintf(stderr, "Slave '%s': Error: bring interface down failed\n", slave_ifname); return res; } if (abi_ver < 2) { /* */ set_if_addr(master_ifname, slave_ifname); } else { res = clear_if_addr(slave_ifname); if (res) { fprintf(stderr, "Slave '%s': Error: clear address failed\n", slave_ifname); return res; } } if (master_mtu.ifr_mtu != slave_mtu.ifr_mtu) { res = set_slave_mtu(slave_ifname, master_mtu.ifr_mtu); if (res) { fprintf(stderr, "Slave '%s': Error: set MTU failed\n", slave_ifname); return res; } } if (hwaddr_set) { /* */ if (abi_ver < 1) { /* */ res = set_slave_hwaddr(slave_ifname, &(master_hwaddr.ifr_hwaddr)); if (res) { fprintf(stderr, "Slave '%s': Error: set hw address " "failed\n", slave_ifname); goto undo_mtu; } /* */ res = set_if_up(slave_ifname, slave_flags.ifr_flags); if (res) { fprintf(stderr, "Slave '%s': Error: bring interface " "down failed\n", slave_ifname); goto undo_slave_mac; } } /* */ } else { /* */ if (abi_ver < 1) { /* */ res = set_if_down(master_ifname, master_flags.ifr_flags); if (res) { fprintf(stderr, "Master '%s': Error: bring interface " "down failed\n", master_ifname); goto undo_mtu; } } res = set_master_hwaddr(master_ifname, &(slave_hwaddr.ifr_hwaddr)); if (res) { fprintf(stderr, "Master '%s': Error: set hw address " "failed\n", master_ifname); goto undo_mtu; } if (abi_ver < 1) { /* */ res = set_if_up(master_ifname, master_flags.ifr_flags); if (res) { fprintf(stderr, "Master '%s': Error: bring interface " "up failed\n", master_ifname); goto undo_master_mac; } } hwaddr_set = 1; } /* */ strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); if ((ioctl(skfd, SIOCBONDENSLAVE, &ifr) < 0) && (ioctl(skfd, BOND_ENSLAVE_OLD, &ifr) < 0)) { saved_errno = errno; v_print("Master '%s': Error: SIOCBONDENSLAVE failed: %s\n", master_ifname, strerror(saved_errno)); res = 1; } if (res) { goto undo_master_mac; } return 0; /* */ undo_master_mac: set_master_hwaddr(master_ifname, &(master_hwaddr.ifr_hwaddr)); hwaddr_set = 0; goto undo_mtu; undo_slave_mac: set_slave_hwaddr(slave_ifname, &(slave_hwaddr.ifr_hwaddr)); undo_mtu: set_slave_mtu(slave_ifname, slave_mtu.ifr_mtu); return res; }
/* CHECK - TODO: how consistent / durable / atomic / recoverable is this * function??? * * This function allows NULL pointers for the element put into the array, * for now. */ uint64_t vector_insert(vector *v, const void *e, vector_comparator cmp) { unsigned long long orig_size, new_size; uint64_t insert_idx, shift_idx; int flush_size = 0; /* HEADS UP: this function is mostly the same as vector_append(), so * if you update one, you should probably update the other! */ if (v == NULL) { v_error("v is NULL\n"); return -1; } if (v->count == VECTOR_MAX_COUNT) { v_error("hit maximum vector length: v->count=%llu\n", v->count); return -1; } #ifndef UNSAFE_COMMIT //#ifdef VECTOR_ASSERT #if 0 /* This assert only makes sense if we're not garbage collecting or * otherwise removing things from vectors. Note that the resizing * code doesn't contain any explicit checks for this (it will * happily reduce the size of the vector below the initial size * if you remove enough things); this bhavior should probably * change, but whatever. */ if (v->size < VECTOR_INIT_SIZE) { v_die("vector size %llu is less than initial size %d!!\n", v->size, VECTOR_INIT_SIZE); } #endif #endif kp_todo("factor out the resizing code that's common to both append " "and insert, dummy!\n"); /* When the last array slot is exhausted, increase the size of the * array by multiplying it by the resize factor. * Resizing the array consists of two steps: A) re-allocing the memory * region, and B) setting the new size of the vector. A) is repeatable, * but unfortunately could result in a memory leak if we don't correctly * remember the pointer AND remember the new size! To minimize the leak * window here, we immediately flush both the pointer and the size (which * should be adjacent to each other in the struct!) after the realloc. * We calculate the size of the flush that we need to perform by using * the offsetof operator, because the compiler may insert padding between * members that we don't know about. This code assumes that the order of * the members in the struct is [data, size, count]. * * ... */ if (v->size == v->count) { #ifdef VECTOR_RESIZE_PRINT v_print("v->size hit v->count = %llu; insert resizing!!!\n", v->size); #endif v_debug("v->size hit v->count = %llu; resizing!!!\n", v->size); /* Check if multiplying v->size by VECTOR_RESIZE_FACTOR will put * it over the VECTOR_MAX_COUNT: */ if (v->size > (VECTOR_MAX_COUNT / VECTOR_RESIZE_FACTOR)) { v_debug("vector size (%llu) times resize factor (%d) would " "overflow max count (%llu), so setting size directly " "to max count\n", v->size, VECTOR_RESIZE_FACTOR, VECTOR_MAX_COUNT); new_size = VECTOR_MAX_COUNT; } else { new_size = v->size * VECTOR_RESIZE_FACTOR; } #ifdef VECTOR_RESIZE_PRINT v_print("calculated new_size=%llu (insert)\n", new_size); #endif orig_size = v->size; #ifdef UNSAFE_COMMIT if (new_size > UNSAFE_COMMIT_LOG_SIZE) { v_print("WARNING: new vector size is greater than %d, probably " "means that we're resizing the commit_log vector!!\n", UNSAFE_COMMIT_LOG_SIZE); } #endif #ifdef V_ASSERT if (new_size > 100) { v_debug("WARNING: resizing vector to new_size=%llu\n", new_size); } #endif /* We expect the flush_size to be 12, but the compiler could possibly * insert padding that changes this. On brief examination, no padding * is inserted and both the data pointer and the size are flushed in * a single flush. * todo: could put the following code segment in its own "pcm_realloc()" * function... */ if (v->use_nvm) { /* We only need to flush data and size; count comes _after_ size, * so use it as the end of the range to flush. */ flush_size = offsetof(vector, count) - offsetof(vector, data); //v_print("calculated flush_size=%d from offsetof(count)=%u, " // "offsetof(data)=%u\n", flush_size, offsetof(vector, count), // offsetof(vector, data)); #ifdef VECTOR_ASSERT if (flush_size < (sizeof(void **) + sizeof(unsigned long long))) { v_die("got unexpected flush_size %d! offsetof(count)=%zu, " "offsetof(data)=%zu\n", flush_size, offsetof(vector, count), offsetof(vector, data)); } //v_print("calculated flush_size %d from offsetof(count)=%u, " // "offsetof(data)=%d\n", flush_size, // offsetof(vector, count), offsetof(vector, data)); #endif } /* Leak window begin. Note that kp_realloc() doesn't flush internally, * because we want to flush both the data and the size in the same * cache line to get consistency * Also note that we don't currently GUARANTEE this, if the compiler * happens to allocate v->data and v->size in two different cache * lines. */ kp_realloc((void **)&(v->data), sizeof(void*) * new_size, v->use_nvm); v->size = new_size; kp_flush_range(&(v->data), flush_size, v->use_nvm); //flush both data and size! /* Leak window end. If we fail after the flush() has returned, * then the next call to vector_append() will skip the resizing * step. */ if (v->data == NULL) { v_error("kp_realloc(array) failed\n"); /* Best effort on failure: reset size to what it was before, * and let caller handle the rest. */ v->size = orig_size; return -1; } v_debug("re-allocated array, now has size %llu (%llu slots)\n", sizeof(void*) * v->size, v->size); } /* We expect that this function will often be an append anyway, so * we start at the end of the array and then search backwards for the * index to insert into. */ insert_idx = v->count; /* The comparator function returns positive if e1 > e2. By using * > and not >= here, we will stop as early as possible, so if * elements happen to be equal to each other then the later elements * will be further along in the array. */ while (insert_idx > 0 && cmp(v->data[insert_idx-1], e) > 0) { insert_idx--; } //v_print("set insert_idx=%llu (count=%llu)\n", insert_idx, v->count); /* Start at the back of the array again and shift elements forward one * at a time. This is somewhat "repeatable" - if a failure occurs while * we're doing this, then we can either remember the shift index, or * look through the array for duplicates, and then resume where we left * off. We flush after every single shift (ouch!) so that no data can * be lost. */ shift_idx = v->count; while (shift_idx > insert_idx) { //v_print("shifting element from %llu to %llu\n", shift_idx-1, shift_idx); v->data[shift_idx] = v->data[shift_idx - 1]; kp_flush_range(&(v->data[shift_idx]), sizeof(void *), v->use_nvm); shift_idx--; } //v_print("now inserting new element into idx=%llu\n", insert_idx); /* Use two flushes here to make this "repeatable" - if we fail after * the first set + flush, there are no real effects (well, this was * true for append... is it any different for insert??). */ v->data[insert_idx] = (void *)e; kp_flush_range(&(v->data[insert_idx]), sizeof(void *), v->use_nvm); v->count++; kp_flush_range(&(v->count), sizeof(unsigned long long), v->use_nvm); //v_print("stored new element %s in slot %llu (now count=%llu, size=%llu)\n", // (char *)(v->data[insert_idx]), insert_idx, v->count, v->size); //v_print("stored new element %p in slot %llu (now count=%llu, size=%llu)\n", // v->data[insert_idx], insert_idx, v->count, v->size); return insert_idx; }
void MoveR::IK4(const point& P) { //ROS_INFO("IK\n"); double th0=0,th1=0,th2=0,th3=0; double x=P.x.data; double y=P.y.data; double z=P.z.data; double p[3]={x,y,z}; double temp1=pow(x,2)+pow(y,2)+pow(z,2); double norm_p=pow(temp1,0.5); double b=acos((pow(lup,2)+pow(ldn,2)-pow(norm_p,2))/(2*lup*ldn)); th3=PI-b; double a=asin((ldn/norm_p)*sin(b)); double s[3]={0,0,0}; double u_z[3]={0,0,1}; double p_s[3]; v_sub(p,s,p_s,3); //ROS_INFO("p\n"); //v_print(p,3); //ROS_INFO("s\n"); //v_print(s,3); //ROS_INFO("p-s\n"); //v_print(p_s,3); double norm_p_s=pow(pow(p_s[0],2)+pow(p_s[1],2)+pow(p_s[2],2),0.5); double u_n[3]={p_s[0]/norm_p_s,p_s[1]/norm_p_s,p_s[2]/norm_p_s}; //ROS_INFO("u_n\n"); //v_print(u_n,3); double tmp[3]; v_scalar_multip(v_dot(u_z,u_n,3),u_n,tmp,3); double u[3],u_u[3]; //v_add(u_z,tmp,u,3); //u correct on 2/17 v_cross(u_n,u_z,u); double norm_u=pow(pow(u[0],2)+pow(u[1],2)+pow(u[2],2),0.5); v_scalar_multip(1/norm_u,u,u_u,3); //ROS_INFO("u_u \n"); //v_print(u_u,3); double v[3],u_v[3]; v_cross(u_n,u_u,v); double norm_v=pow(pow(v[0],2)+pow(v[1],2)+pow(v[2],2),0.5); v_scalar_multip(1/norm_v,v,u_v,3); //ROS_INFO("u_v \n"); //v_print(u_v,3); double c[3]; v_scalar_multip(cos(a)*lup,u_n,tmp,3); v_add(s,tmp,c,3); double r=lup*sin(a); //double fi=PI/2; ROS_INFO("b %f\n",b); ROS_INFO("a %f\n",a); double tmp1[3],tmp2[3]; v_scalar_multip(cos(fi),u_u,tmp1,3); v_scalar_multip(sin(fi),u_v,tmp2,3); v_add(tmp1,tmp2,tmp,3); v_scalar_multip(r,tmp,tmp,3); double e[3]; v_add(c,tmp,e,3); ROS_INFO("u_u \n"); v_print(u_u,3); double ex=e[0],ey=e[1],ez=e[2]; ROS_INFO("ex %f\n",ex); ROS_INFO("ey %f\n",ey); ROS_INFO("ez %f\n",ez); if(ex>=0&&ey<0) th0=atan((double)abs(ex)/abs(ey)); else if(ex>=0&&ey>=0) th0=PI-atan((double)abs(ex)/abs(ey)); else if(ex<0&&ey>=0) th0=PI+atan((double)abs(ex)/abs(ey)); else if(ex<0&&ey<0) th0=-atan((double)abs(ex)/abs(ey)); double temp2=pow(pow(ex,2)+pow(ey,2),0.5); if(ez>=0) th1=-atan((double)abs(ez)/temp2); else if(ez<0) th1=atan((double)abs(ez)/temp2); //ROS_INFO("IK:th0 %f\n",th0); //ROS_INFO("IK:th1 %f\n",th1); //ROS_INFO("th3 %f\n",th3); th2=atan2(-x*sin(th0)*sin(th1)+y*cos(th0)*sin(th1)-z*cos(th1),x*cos(th0)+y*sin(th0)); ROS_INFO("x %f y %f z %f\n",x,y,z); ROS_INFO("th0 %f th1 %f th2 %f th3 %f fi% f\n",th0,th1,th2,th3,fi); if(isnan(th0)||isnan(th1)||isnan(th2)||isnan(th3)||th0>TH0_MAX||th0<TH0_MIN||th1>TH1_MAX||th1<TH1_MIN||th3>TH3_MAX||th3<TH3_MIN){ ROS_INFO("Fail and dont move\n"); } else{ ROS_INFO("go move move time=%f\n",move_time); arm_angle.joint0.angle = th0; arm_angle.joint0.duration = move_time; arm_angle.joint1.angle = th1; arm_angle.joint1.duration = move_time; arm_angle.joint2.angle = th2; arm_angle.joint2.duration = move_time; arm_angle.joint3.angle = th3; arm_angle.joint3.duration = move_time; } }
void init_physics() { ions[0] = proton(); ions[1] = electron(); ions[0].location = (struct vector) {0,0,0}; ions[0].velocity=(struct vector){0,0,0}; ions[1].location = (struct vector) {0,20,0}; ions[1].velocity=(struct vector){0.15,0,0}; } /* void init_physics() { #define RANDO ((rand()%100)-50) int i; for(i=0;i<N_IONS;i++){ switch(rand()%8){ case 0: ions[i] = nuclion(rand()%9); break; default: ions[i] = electron(); break; } ions[i].location = (struct vector) {RANDO,RANDO,RANDO}; ions[i].velocity=(struct vector){0,0,0}; } } */ void dump_state() { int i; printf("--- IONS ---\n"); for(i=0;i<N_IONS;i++){ printf("[%i]\n",i); printf("charge %Lf \n",ions[i].charge); printf("mass %Lf \n",ions[i].mass); v_print("location",ions[i].location); v_printe("velocity",ions[i].velocity); v_printe("acceleration",ions[i].accel); } printf("-----------\n"); } void calculate_acceleration(struct particle * p,double dt) { struct vector fv = {0,0,0}; int i; for(i=0;i<N_IONS;i++){ fv = v_add(fv,gravitation(ions[i],*p)); fv = v_add(fv,coulombs(ions[i],*p)); // fv = v_add(fv,biotsavart(ions[i],*p)); } /* because f/m=a */ fv.x/=p->mass; fv.y/=p->mass; fv.z/=p->mass; p->accel = v_scalar_mul(dt,fv); } void apply_vel(struct particle * p) { p->velocity = v_add(p->accel,p->velocity); p->location = v_add(p->location,p->velocity); } void physics_tick(double dt) { int i; for(i=0;i<N_IONS;i++) calculate_acceleration(&ions[i],dt); for(i=0;i<N_IONS;i++) apply_vel(&ions[i]); if(keys['P']) dump_state(); }
int main(int argc, char *argv[]) { char **spp, *master_ifname, *slave_ifname; int c, i, rv; int res = 0; int exclusive = 0; while ((c = getopt_long(argc, argv, "acdfhuvV", longopts, 0)) != EOF) { switch (c) { case 'a': opt_a++; exclusive++; break; case 'c': opt_c++; exclusive++; break; case 'd': opt_d++; exclusive++; break; case 'f': opt_f++; exclusive++; break; case 'h': opt_h++; exclusive++; break; case 'u': opt_u++; exclusive++; break; case 'v': opt_v++; break; case 'V': opt_V++; exclusive++; break; case '?': fprintf(stderr, "%s", usage_msg); res = 2; goto out; } } /* */ if (exclusive > 1) { fprintf(stderr, "%s", usage_msg); res = 2; goto out; } if (opt_v || opt_V) { printf("%s", version); if (opt_V) { res = 0; goto out; } } if (opt_u) { printf("%s", usage_msg); res = 0; goto out; } if (opt_h) { printf("%s", usage_msg); printf("%s", help_msg); res = 0; goto out; } /* */ if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); res = 1; goto out; } if (opt_a) { if (optind == argc) { /* */ /* */ if_print((char *)NULL); goto out; } else { /* */ fprintf(stderr, "%s", usage_msg); res = 2; goto out; } } /* */ spp = argv + optind; master_ifname = *spp++; if (master_ifname == NULL) { fprintf(stderr, "%s", usage_msg); res = 2; goto out; } /* */ res = get_drv_info(master_ifname); if (res) { fprintf(stderr, "Master '%s': Error: handshake with driver failed. " "Aborting\n", master_ifname); goto out; } slave_ifname = *spp++; if (slave_ifname == NULL) { if (opt_d || opt_c) { fprintf(stderr, "%s", usage_msg); res = 2; goto out; } /* */ if_print(master_ifname); goto out; } res = get_if_settings(master_ifname, master_ifra); if (res) { /* */ fprintf(stderr, "Master '%s': Error: get settings failed: %s. " "Aborting\n", master_ifname, strerror(res)); goto out; } /* */ if (!(master_flags.ifr_flags & IFF_MASTER)) { fprintf(stderr, "Illegal operation; the specified interface '%s' " "is not a master. Aborting\n", master_ifname); res = 1; goto out; } /* */ if (!(master_flags.ifr_flags & IFF_UP)) { fprintf(stderr, "Illegal operation; the specified master interface " "'%s' is not up.\n", master_ifname); res = 1; goto out; } /* */ if (!opt_c && !opt_d) { sa_family_t master_family = master_hwaddr.ifr_hwaddr.sa_family; unsigned char *hwaddr = (unsigned char *)master_hwaddr.ifr_hwaddr.sa_data; /* */ if (master_family != 1 && !opt_f) { fprintf(stderr, "Illegal operation: The specified master " "interface '%s' is not ethernet-like.\n " "This program is designed to work with " "ethernet-like network interfaces.\n " "Use the '-f' option to force the " "operation.\n", master_ifname); res = 1; goto out; } /* */ for (i = 0; i < 6; i++) { if (hwaddr[i] != 0) { hwaddr_set = 1; break; } } if (hwaddr_set) { v_print("current hardware address of master '%s' " "is %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " "type %d\n", master_ifname, hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5], master_family); } } /* */ if (opt_c) { /* */ res = get_slave_flags(slave_ifname); if (res) { fprintf(stderr, "Slave '%s': Error: get flags failed. " "Aborting\n", slave_ifname); goto out; } res = change_active(master_ifname, slave_ifname); if (res) { fprintf(stderr, "Master '%s', Slave '%s': Error: " "Change active failed\n", master_ifname, slave_ifname); } } else { /* */ do { if (opt_d) { /* */ rv = get_slave_flags(slave_ifname); if (rv) { /* */ /* */ fprintf(stderr, "Slave '%s': Error: get flags " "failed. Skipping\n", slave_ifname); res = rv; continue; } rv = release(master_ifname, slave_ifname); if (rv) { fprintf(stderr, "Master '%s', Slave '%s': Error: " "Release failed\n", master_ifname, slave_ifname); res = rv; } } else { /* */ rv = get_if_settings(slave_ifname, slave_ifra); if (rv) { /* */ /* */ fprintf(stderr, "Slave '%s': Error: get " "settings failed: %s. " "Skipping\n", slave_ifname, strerror(rv)); res = rv; continue; } rv = enslave(master_ifname, slave_ifname); if (rv) { fprintf(stderr, "Master '%s', Slave '%s': Error: " "Enslave failed\n", master_ifname, slave_ifname); res = rv; } } } while ((slave_ifname = *spp++) != NULL); } out: if (skfd >= 0) { close(skfd); } return res; }
static int enslave(char *master_ifname, char *slave_ifname) { struct ifreq ifr; int res = 0; if (slave_flags.ifr_flags & IFF_SLAVE) { fprintf(stderr, "Illegal operation: The specified slave interface " "'%s' is already a slave\n", slave_ifname); return 1; } res = set_if_down(slave_ifname, slave_flags.ifr_flags); if (res) { fprintf(stderr, "Slave '%s': Error: bring interface down failed\n", slave_ifname); return res; } if (abi_ver < 2) { /* Older bonding versions would panic if the slave has no IP * address, so get the IP setting from the master. */ set_if_addr(master_ifname, slave_ifname); } else { res = clear_if_addr(slave_ifname); if (res) { fprintf(stderr, "Slave '%s': Error: clear address failed\n", slave_ifname); return res; } } if (master_mtu.ifr_mtu != slave_mtu.ifr_mtu) { res = set_slave_mtu(slave_ifname, master_mtu.ifr_mtu); if (res) { fprintf(stderr, "Slave '%s': Error: set MTU failed\n", slave_ifname); return res; } } if (hwaddr_set) { /* Master already has an hwaddr * so set it's hwaddr to the slave */ if (abi_ver < 1) { /* The driver is using an old ABI, so * the application sets the slave's * hwaddr */ res = set_slave_hwaddr(slave_ifname, &(master_hwaddr.ifr_hwaddr)); if (res) { fprintf(stderr, "Slave '%s': Error: set hw address " "failed\n", slave_ifname); goto undo_mtu; } /* For old ABI the application needs to bring the * slave back up */ res = set_if_up(slave_ifname, slave_flags.ifr_flags); if (res) { fprintf(stderr, "Slave '%s': Error: bring interface " "down failed\n", slave_ifname); goto undo_slave_mac; } } /* The driver is using a new ABI, * so the driver takes care of setting * the slave's hwaddr and bringing * it up again */ } else { /* No hwaddr for master yet, so * set the slave's hwaddr to it */ if (abi_ver < 1) { /* For old ABI, the master needs to be * down before setting its hwaddr */ res = set_if_down(master_ifname, master_flags.ifr_flags); if (res) { fprintf(stderr, "Master '%s': Error: bring interface " "down failed\n", master_ifname); goto undo_mtu; } } res = set_master_hwaddr(master_ifname, &(slave_hwaddr.ifr_hwaddr)); if (res) { fprintf(stderr, "Master '%s': Error: set hw address " "failed\n", master_ifname); goto undo_mtu; } if (abi_ver < 1) { /* For old ABI, bring the master * back up */ res = set_if_up(master_ifname, master_flags.ifr_flags); if (res) { fprintf(stderr, "Master '%s': Error: bring interface " "up failed\n", master_ifname); goto undo_master_mac; } } hwaddr_set = 1; } /* Do the real thing */ strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); if ((ioctl(skfd, SIOCBONDENSLAVE, &ifr) < 0) && (ioctl(skfd, BOND_ENSLAVE_OLD, &ifr) < 0)) { saved_errno = errno; v_print("Master '%s': Error: SIOCBONDENSLAVE failed: %s\n", master_ifname, strerror(saved_errno)); res = 1; } if (res) { goto undo_master_mac; } return 0; /* rollback (best effort) */ undo_master_mac: set_master_hwaddr(master_ifname, &(master_hwaddr.ifr_hwaddr)); hwaddr_set = 0; goto undo_mtu; undo_slave_mac: set_slave_hwaddr(slave_ifname, &(slave_hwaddr.ifr_hwaddr)); undo_mtu: set_slave_mtu(slave_ifname, slave_mtu.ifr_mtu); return res; }