1. Current status:
Using iscsiadm one cannot do any network configuration for qla4xxx adapters.
However an iface is created for the qla4xxx ports using the hwaddress.
\# ls /etc/iscsi/ifaces/
iface.example iface0 qla4xxx.00:0e:1e:04:8b:2a qla4xxx.00:0e:1e:04:8b:2e This allows user to issue sendtargets via the qla4xxx iscsi offload.
2. Current Proposal:
Current proposal is to allow iscsiadm to configure the network settings for qla4xxx ports.
This implementation is based on discussions at
http://marc.info/?l=linux-scsi&m=127066184916180&w=2
http://groups.google.com/group/open-iscsi/browse_thread/thread/d8e8c2df71c95d69/8f731d95d46141a0?lnk=gst&q=iscsi+hba#
2.1 Changes in iscsiadm/iscsid
2.1.1 Add a new event: ISCSI_UEVENT_SET_NET_CONFIG
2.1.2 New structure to represent a single network parameter
/* iSCSI network params */
enum iscsi_net_param_type {
ISCSI_NET_PARAM_IPV4_ADDR = 1,
ISCSI_NET_PARAM_IPV4_SUBNET = 2,
ISCSI_NET_PARAM_IPV4_GW = 3,
ISCSI_NET_PARAM_IPV4_BOOTPROTO = 4,
ISCSI_NET_PARAM_VLAN = 5,
ISCSI_NET_PARAM_MAC = 6,
ISCSI_NET_PARAM_IPV6_LINKLOCAL = 7,
ISCSI_NET_PARAM_IPV6_ADDR = 8,
ISCSI_NET_PARAM_IPV6_ROUTER = 9,
ISCSI_NET_PARAM_IPV6_AUTOCFG = 10,
ISCSI_NET_PARAM_NET_STATE = 11,
};
struct iscsi_net_param {
uint32_t param_type; /* enum iscsi_net_param */
uint32_t iface_type; /* IPv4 or IPv6 */
uint32_t iface_num; /* iface number, 0 - n */
uint32_t length; /* Actual length of the param */
uint32_t offset; /* For param with length > 256 */
uint8_t value[256]; /* max is 223 iscsi name */ } __attribute__((packed));
} __attribute__((packed));
2.1.3 New parameters in iface
Added following params:-
typedef struct iface_rec {
struct list_head list;
/* iscsi iface record name */
char name[ISCSI_MAX_IFACE_LEN];
+ char iface_num[ISCSI_MAX_STR_LEN];
/* network layer iface name (eth0) */
char netdev[IFNAMSIZ];
char ipaddress[NI_MAXHOST];
+ char subnet_mask[NI_MAXHOST];
+ char gateway[NI_MAXHOST];
+ char bootproto[ISCSI_MAX_STR_LEN];
+ char ipv6_linklocal[NI_MAXHOST];
+ char ipv6_router[NI_MAXHOST];
+ char autocfg[NI_MAXHOST];
+ char vlan[ISCSI_MAX_STR_LEN];
+ char state[ISCSI_MAX_STR_LEN]; /* 0 = disable,
+ * 1 = enable */
2.1.4 Change in operations
Add two new operations to iscsiadm
apply: Apply the single iface settings
applyall: Apply the iface settings of all iface having the same MAC address
2.2 Changes in sysfs network representation The new sysfs directory would look like thisi:-
/sys/class/iscsi_iface/
|
|- ipv4-iface-<host_no>-<iface_no>/ <-- for ipv4
|- ipaddress
|- subnet
|- gateway
|-bootproto
|- state
|- ipv6-iface-<host_no>-<iface_no>/ <-- for ipv6
|- ipaddress
|- link_local_addr
|- router_addr
|- state
3. Flow:
3.1 User space code:
- If user specify --op=update, then just update the iface config file
- If use specify --op=applyall then read ifaces having the same hwaddress
and build up the net config buffer.
- Note: If --op is "apply" then only settings for single iface is read,
the iface provided with -I option is only read.
- The net config buffer will look like this.
----------------------------------------------------------------|
| iscsi_net_param { |
| param = ISCSI_NET_PARAM_IPV4_ADDR; |
| iface_num = 0; |
| length = 4 |
| offset = 0; |
| value[0] = ipaddress[0]; |
| value[1] = ipaddress[1]; |
| value[2] = ipaddress[2]; |
| value[3] = ipaddress[3]; |
| } |
----------------------------------------------------------------|
| iscsi_net_param { |
| param = ISCSI_NET_PARAM_IPV4_GW; |
| iface_num = 0; |
| length = 4 |
| offset = 0; |
| value[0] = ipgateway[0]; |
| value[1] = ipgateway[1]; |
| value[2] = ipgateway[2]; |
| value[3] = ipgateway[3]; |
| } |
-----------------------------------------------------------------
| |
| iscsi_net_param { |
| param = ISCSI_NET_PARAM_IPV4_ADDR; |
| iface_num = 1; |
| length = 4 |
| offset = 0; |
| value[0] = ipaddress[0]; |
| value[1] = ipaddress[1]; |
| value[2] = ipaddress[2]; |
| value[3] = ipaddress[3]; |
| } |
-----------------------------------------------------------------
| iscsi_net_param { |
| param = ISCSI_NET_PARAM_IPV4_GW; |
| iface_num = 1; |
| length = 4 |
| offset = 0; |
| value[0] = ipgateway[0]; |
| value[1] = ipgateway[1]; |
| value[2] = ipgateway[2]; |
| value[3] = ipgateway[3]; |
| } |
-----------------------------------------------------------------
| iscsi_net_param { |
| param = ISCSI_NET_PARAM_NET_STATE; |
| iface_num = 1; |
| length = 1 |
| offset = 0; |
| value[0] = 0; /* 0 = disable, default = 1 = enable |
| } |
-----------------------------------------------------------------
And so on....
The total size of this will be addition of sizeof each iscsi_net_param
And this will be the payload
ev = (iscsi_uevent *)net_param_buf;
ev.msg_setnet_param.host_no with be the scsi_host number
ev.msg_set_net_param.count = Total number of net params.
- This ev need to be sent down with event type = ISCSI_UEVENT_SET_NET_CONFIG
3.2 Kernel space code:
- Once event is received, the buffer will look like as explained above
- the scsi_transport_iscsi would call the adapters transport->set_net_config
- In set_net_config each individual param can be decoded and set into the
hardware.
4. qla4xxx configuration:
iscsid, creates the iface for qla4xxx, based on the hwaddress. To display
the iface related to qla4xxx execute following
# iscsiadm -m iface
qla4xxx.00:0e:1e:04:8b:2e qla4xxx,00:0e:1e:04:8b:2e,<empty>,<empty>,<empty>
qla4xxx.00:0e:1e:04:8b:2e.ipv6 qla4xxx,00:0e:1e:04:8b:2e,<empty>,<empty>,<empty>
qla4xxx.00:0e:1e:04:8b:2a qla4xxx,00:0e:1e:04:8b:2a,20.15.0.66,<empty>,<empty>
qla4xxx.00:0e:1e:04:8b:2a.ipv6
qla4xxx,00:0e:1e:04:8b:2a,2001:DB8:1111:2222::8888,<empty>,<empty>
qla4xxx.00:0e:1e:04:8b:2a.ipv6.1
qla4xxx,00:0e:1e:04:8b:2a,2001:DB8:4444:5555::9999,<empty>,<empty>
To setup network configuration there can be two methods 4. 1. User can manually modify the iface file, and issue an "apply" command.
---------------------------------------------------------------------------
iface.example iface0 qla4xxx.00:0e:1e:04:8b:2a qla4xxx.00:0e:1e:04:8b:2e
Example:
iface.ipaddress = 192.168.2.2 (decimal)
iface.iface_num = 0 (default)
iface.subnetmask = 255.255.255.0 (decimal)
iface.vlan = 0x1022 (hex)
# vi qla4xxx.00:0e:1e:04:8b:2a.ipv6
If file does not exisit the one can create it.
iface.ipaddress = 1111:2222::7777:8888 (hex)
iface.iface_num = 0
iface.vlan = 0x1022 (hex)
# iscsiadm -m iface -I qla4xxx.00:0e:1e:04:8b:2a --op=applyall
This will read the iface config file and apply the settings to the
hardware.
Note, this will read all the iface belonging to the same MAC address.
4.2. User can use iscsiadm to specify the values and then apply
--------------------------------------------------------------
# ls /etc/iscsi/ifaces/
iface.example iface0 qla4xxx.00:0e:1e:04:8b:2a qla4xxx.00:0e:1e:04:8b:2e
# iscsiadm -m iface -I qla4xxx.00:0e:1e:04:8b:2a -o update \
-n iface.ipaddress -v 192.168.1.2
# iscsiadm -m iface -I qla4xxx.00:0e:1e:04:8b:2a -o update \
-n iface.gateway -v 192.168.1.1
# iscsiadm -m iface -I qla4xxx.00:0e:1e:04:8b:2a -o update \
-n iface.subnet_mask -v 255.255.255.0
# iscsiadm -m iface -I qla4xxx.00:0e:1e:04:8b:2a -o applyall
Setting up multiple IP:
First interface (default, no need to set iface_num, it is 0 by default)
# iscsiadm -m iface -I qla4xxx.00:0e:1e:04:8b:2a -o update \
-n iface.ipaddress -v 192.168.1.2
Create the second one if it does not exist
# iscsiadm -m iface -I qla4xxx.00:0e:1e:04:8b:2a.1 -op=new
# iscsiadm -m iface -I qla4xxx.00:0e:1e:04:8b:2a -o update \
-n iface.iface_num -v 1 (Mandatory)
# iscsiadm -m iface -I qla4xxx.00:0e:1e:04:8b:2a -o update \
-n iface.ipaddress -v 192.168.1.3
# iscsiadm -m iface -I qla4xxx.00:0e:1e:04:8b:2a --op=applyall
Note: If there are common settings for multiple interfaces then the
settings from 0th iface would be considered valid.
Note: To apply settings for a single iface, just say --op=apply
Signed-off-by: Lalit Chandivade <lalit.ch...@qlogic.com>
Signed-off-by: Harish Zunjarrao <harish.z...@qlogic.com>
Signed-off-by: Vikas Chaudhary <vikas.c...@qlogic.com>
---
include/iscsi_if.h | 44 +++++++++
usr/config.h | 11 ++
usr/idbm.c | 10 ++
usr/idbm_fields.h | 5 +
usr/iface.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++++++++
usr/iface.h | 2 +
usr/iscsi_ipc.h | 3 +
usr/iscsiadm.c | 80 ++++++++++++++++-
usr/iscsid_req.c | 3 +-
usr/mgmt_ipc.c | 23 +++++
usr/mgmt_ipc.h | 5 +
usr/netlink.c | 153 ++++++++++++++++++++++++-------
12 files changed, 561 insertions(+), 36 deletions(-)
diff --git a/include/iscsi_if.h b/include/iscsi_if.h
index 50a09cb..b04eba2 100644
--- a/include/iscsi_if.h
+++ b/include/iscsi_if.h
@@ -64,6 +64,7 @@ enum iscsi_uevent_e {
ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST = UEVENT_BASE + 19,
ISCSI_UEVENT_PATH_UPDATE = UEVENT_BASE + 20,
+ ISCSI_UEVENT_SET_NET_CONFIG = UEVENT_BASE + 21,
ISCSI_UEVENT_MAX = ISCSI_UEVENT_PATH_UPDATE,
@@ -181,6 +182,10 @@ struct iscsi_uevent {
struct msg_set_path {
uint32_t host_no;
} set_path;
+ struct msg_set_net_config {
+ uint32_t host_no;
+ uint32_t count;
+ } set_net_config;
} u;
union {
/* messages k -> u */
@@ -246,6 +251,45 @@ struct iscsi_path {
uint16_t pmtu;
} __attribute__ ((aligned (sizeof(uint64_t))));
+#define ISCSI_BOOTPROTO_STATIC 0x0
+#define ISCSI_BOOTPROTO_DHCP 0x1
+
+#define ISCSI_IPv6_AUTOCFG_LINKLOCAL_ADDR 0x01
+#define ISCSI_IPv6_AUTOCFG_ND_ADDR_ENABLE 0x02
+#define ISCSI_IPv6_AUTOCFG_DFLT_ROUTER_ADDR 0x04
+
+/* iSCSI network params */
+enum iscsi_net_param_type {
+ ISCSI_NET_PARAM_IPV4_ADDR = 1,
+ ISCSI_NET_PARAM_IPV4_SUBNET = 2,
+ ISCSI_NET_PARAM_IPV4_GW = 3,
+ ISCSI_NET_PARAM_IPV4_BOOTPROTO = 4,
+ ISCSI_NET_PARAM_VLAN = 5,
+ ISCSI_NET_PARAM_MAC = 6,
+ ISCSI_NET_PARAM_IPV6_LINKLOCAL = 7,
+ ISCSI_NET_PARAM_IPV6_ADDR = 8,
+ ISCSI_NET_PARAM_IPV6_ROUTER = 9,
+ ISCSI_NET_PARAM_IPV6_AUTOCFG = 10,
+ ISCSI_NET_PARAM_NET_STATE = 11,
+};
+
+/* 20 param per iface * 5 iface per port = 100 params */
+#define ISCSI_MAX_IFACE_PER_HW 5
+#define ISCSI_MAX_PARAM_PER_IFACE 20
+#define ISCSI_MAX_NET_PARAMS (ISCSI_MAX_IFACE_PER_HW * \
+ ISCSI_MAX_PARAM_PER_IFACE)
+
+#define IFACE_TYPE_IPV4 0
+#define IFACE_TYPE_IPV6 1
+struct iscsi_net_param {
+ uint32_t param_type; /* enum iscsi_net_param */
+ uint32_t iface_type; /* IPv4 or IPv6 */
+ uint32_t iface_num; /* iface number, 0 - n */
+ uint32_t length; /* Actual length of the param */
+ uint32_t offset; /* For param with length > 256 */
+ uint8_t value[256]; /* max is 223 iscsi name */
+} __attribute__((packed));
+
/*
* Common error codes
*/
diff --git a/usr/config.h b/usr/config.h
index 5cb4d56..8ed71be 100644
--- a/usr/config.h
+++ b/usr/config.h
@@ -196,14 +196,25 @@ typedef struct session_rec {
} session_rec_t;
#define ISCSI_TRANSPORT_NAME_MAXLEN 16
+#define ISCSI_MAX_STR_LEN 80
typedef struct iface_rec {
struct list_head list;
/* iscsi iface record name */
char name[ISCSI_MAX_IFACE_LEN];
+ char iface_num[ISCSI_MAX_STR_LEN];
/* network layer iface name (eth0) */
char netdev[IFNAMSIZ];
char ipaddress[NI_MAXHOST];
+ char subnet_mask[NI_MAXHOST];
+ char gateway[NI_MAXHOST];
+ char bootproto[ISCSI_MAX_STR_LEN];
+ char ipv6_linklocal[NI_MAXHOST];
+ char ipv6_router[NI_MAXHOST];
+ char autocfg[NI_MAXHOST];
+ char vlan[ISCSI_MAX_STR_LEN];
+ char state[ISCSI_MAX_STR_LEN]; /* 0 = disable,
+ * 1 = enable */
/*
* TODO: we may have to make this bigger and interconnect
* specific for infinniband
diff --git a/usr/idbm.c b/usr/idbm.c
index a73b410..7fa2cc6 100644
--- a/usr/idbm.c
+++ b/usr/idbm.c
@@ -370,6 +370,16 @@ void idbm_recinfo_iface(iface_rec_t *r, recinfo_t *ri)
__recinfo_str(IFACE_TRANSPORTNAME, ri, r, transport_name,
IDBM_SHOW, num, 1);
__recinfo_str(IFACE_INAME, ri, r, iname, IDBM_SHOW, num, 1);
+ __recinfo_str(IFACE_BOOT_PROTO, ri, r, bootproto, IDBM_SHOW, num, 1);
+ __recinfo_str(IFACE_SUBNET_MASK, ri, r, subnet_mask,
+ IDBM_SHOW, num, 1);
+ __recinfo_str(IFACE_GATEWAY, ri, r, gateway, IDBM_SHOW, num, 1);
+ __recinfo_str(IFACE_LINKLOCAL, ri, r, ipv6_linklocal,
+ IDBM_SHOW, num, 1);
+ __recinfo_str(IFACE_ROUTER, ri, r, ipv6_router, IDBM_SHOW, num, 1);
+ __recinfo_str(IFACE_STATE, ri, r, state, IDBM_SHOW, num, 1);
+ __recinfo_str(IFACE_VLAN, ri, r, vlan, IDBM_SHOW, num, 1);
+ __recinfo_str(IFACE_NUM, ri, r, iface_num, IDBM_SHOW, num, 1);
}
recinfo_t *idbm_recinfo_alloc(int max_keys)
diff --git a/usr/idbm_fields.h b/usr/idbm_fields.h
index eaa55d1..6e648f2 100644
--- a/usr/idbm_fields.h
+++ b/usr/idbm_fields.h
@@ -76,6 +76,11 @@
#define IFACE_PRIMARY_DNS "iface.primary_dns"
#define IFACE_SEC_DNS "iface.secondary_dns"
#define IFACE_VLAN "iface.vlan"
+#define IFACE_LINKLOCAL "iface.ipv6_linklocal"
+#define IFACE_ROUTER "iface.ipv6_router"
+#define IFACE_AUTOCFG "iface.autocfg"
+#define IFACE_STATE "iface.state"
+#define IFACE_NUM "iface.iface_num"
/* discovery fields */
#define DISC_STARTUP "discovery.startup"
diff --git a/usr/iface.c b/usr/iface.c
index 8a1683b..ebbdef2 100644
--- a/usr/iface.c
+++ b/usr/iface.c
@@ -26,6 +26,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <arpa/inet.h>
#include "log.h"
#include "list.h"
@@ -885,3 +886,260 @@ fail:
}
return rc;
}
+
+/* Network state: disable/enable */
+static inline void __iface_fill_net_state(struct iscsi_net_param *net_param,
+ struct iface_rec *iface, uint32_t iface_type)
+{
+
+ memset(net_param, 0, sizeof(*net_param));
+ net_param->param_type = ISCSI_NET_PARAM_NET_STATE;
+ net_param->iface_type = iface_type;
+ net_param->length = 1;
+ if (strcmp(iface->state, "disable") == 0)
+ net_param->value[0] = 0;
+ else /* Assume enabled */
+ net_param->value[0] = 1;
+
+ sscanf(iface->iface_num, "%u", &net_param->iface_num);
+}
+
+/* Bootproto , need to send down the valuse as set or not set*/
+static inline int __iface_fill_net_bootproto(struct iscsi_net_param *net_param,
+ struct iface_rec *iface)
+{
+ memset(net_param, 0, sizeof(*net_param));
+
+ if (strcmp(iface->bootproto, "dhcp") == 0)
+ net_param->value[0] = ISCSI_BOOTPROTO_DHCP;
+
+ net_param->param_type = ISCSI_NET_PARAM_IPV4_BOOTPROTO;
+ net_param->iface_type = IFACE_TYPE_IPV4;
+ net_param->length = 1;
+ sscanf(iface->iface_num, "%u", &net_param->iface_num);
+ return 0;
+}
+
+/* Autocfg */
+static inline int __iface_fill_net_autocfg(struct iscsi_net_param *net_param,
+ struct iface_rec *iface)
+{
+ memset(net_param, 0, sizeof(*net_param));
+ net_param->param_type = ISCSI_NET_PARAM_IPV6_AUTOCFG;
+ net_param->iface_type = IFACE_TYPE_IPV6;
+ net_param->length = 1;
+
+ if (strcmp(iface->autocfg, "nd") == 0)
+ net_param->value[0] |= ISCSI_IPv6_AUTOCFG_ND_ADDR_ENABLE;
+ if (strcmp(iface->ipv6_linklocal, "auto") == 0)
+ net_param->value[0] |= ISCSI_IPv6_AUTOCFG_LINKLOCAL_ADDR;
+ if (strcmp(iface->ipv6_router, "auto") == 0)
+ net_param->value[0] |= ISCSI_IPv6_AUTOCFG_DFLT_ROUTER_ADDR;
+
+ return 0;
+}
+
+
+/* Network IPv4 address: 4 bytes */
+static inline int __iface_fill_net_ipv4_addr(struct iscsi_net_param *net_param,
+ struct iface_rec *iface,
+ uint32_t param_type)
+{
+ int rc = 1;
+ memset(net_param, 0, sizeof(*net_param));
+ net_param->param_type = param_type;
+ net_param->iface_type = IFACE_TYPE_IPV4;
+ net_param->length = 4;
+ switch (param_type) {
+ case ISCSI_NET_PARAM_IPV4_ADDR:
+ rc = inet_pton(AF_INET, iface->ipaddress, net_param->value);
+ if (rc <= 0)
+ return 1;
+ break;
+ case ISCSI_NET_PARAM_IPV4_SUBNET:
+ rc = inet_pton(AF_INET, iface->subnet_mask, net_param->value);
+ if (rc <= 0)
+ return 1;
+ break;
+ case ISCSI_NET_PARAM_IPV4_GW:
+ rc = inet_pton(AF_INET, iface->gateway, net_param->value);
+ if (rc <= 0)
+ return 1;
+ break;
+
+ }
+
+ sscanf(iface->iface_num, "%u", &net_param->iface_num);
+
+ /* validate */
+ if (!net_param->value[0] && !net_param->value[1] &&
+ !net_param->value[2] && !net_param->value[3])
+ return 1;
+ return 0;
+}
+
+static inline int __iface_fill_net_ipv6_addr(struct iscsi_net_param *net_param,
+ struct iface_rec *iface,
+ uint32_t param_type)
+{
+ int rc;
+ memset(net_param, 0, sizeof(*net_param));
+ net_param->param_type = param_type;
+ net_param->iface_type = IFACE_TYPE_IPV6;
+ switch (param_type) {
+ case ISCSI_NET_PARAM_IPV6_ADDR:
+ net_param->length = 16;
+ rc = inet_pton(AF_INET6, iface->ipaddress, net_param->value);
+ if (rc <= 0)
+ return 1;
+ break;
+ case ISCSI_NET_PARAM_IPV6_LINKLOCAL:
+ net_param->length = 16;
+ rc = inet_pton(AF_INET6, iface->ipv6_linklocal,
+ net_param->value);
+ if (rc <= 0)
+ return 1;
+ break;
+ case ISCSI_NET_PARAM_IPV6_ROUTER:
+ net_param->length = 16;
+ rc = inet_pton(AF_INET6, iface->ipv6_router, net_param->value);
+ if (rc <= 0)
+ return 1;
+ break;
+
+ }
+
+ sscanf(iface->iface_num, "%u", &net_param->iface_num);
+
+ return 0;
+}
+
+
+struct iface_net_config {
+ struct iface_rec *primary;
+ char *cfg_data;
+ int count;
+};
+
+static int __iface_build_net_config(void *data, struct iface_rec *iface)
+{
+ struct iface_net_config *net_config = data;
+ struct iscsi_net_param *net_param;
+ int rc;
+ int state_set = 0;
+
+ if (strcmp(net_config->primary->hwaddress, iface->hwaddress))
+ return 0;
+
+ if (net_config->count > ISCSI_MAX_NET_PARAMS)
+ return 1;
+
+ net_param = (struct iscsi_net_param *)((uint8_t *)net_config->cfg_data +
+ (sizeof(struct iscsi_net_param) * net_config->count));
+
+ if (__iface_fill_net_bootproto(net_param, iface) == 0) {
+ net_param++;
+ net_config->count++;
+
+ __iface_fill_net_state(net_param, iface, IFACE_TYPE_IPV4);
+ net_param++;
+ net_config->count++;
+ state_set = 1;
+
+ }
+
+ if (__iface_fill_net_autocfg(net_param, iface) == 0) {
+ net_param++;
+ net_config->count++;
+
+ __iface_fill_net_state(net_param, iface, IFACE_TYPE_IPV6);
+ net_param++;
+ net_config->count++;
+ state_set = 1;
+ }
+
+ if (strstr(iface->ipaddress, ":") != NULL ||
+ strstr(iface->ipv6_linklocal, ":") != NULL ||
+ strstr(iface->ipv6_router, ":") != NULL) {
+ /* IPv6 manual settings */
+ if (__iface_fill_net_ipv6_addr(net_param, iface,
+ ISCSI_NET_PARAM_IPV6_ADDR) == 0) {
+ net_param++;
+ net_config->count++;
+ }
+
+ if (__iface_fill_net_ipv6_addr(net_param, iface,
+ ISCSI_NET_PARAM_IPV6_LINKLOCAL) == 0) {
+ net_param++;
+ net_config->count++;
+ }
+
+ if (__iface_fill_net_ipv6_addr(net_param, iface,
+ ISCSI_NET_PARAM_IPV6_ROUTER) == 0) {
+ net_param++;
+ net_config->count++;
+ }
+
+ if (!state_set) {
+ __iface_fill_net_state(net_param, iface,
+ IFACE_TYPE_IPV6);
+ net_param++;
+ net_config->count++;
+ }
+
+ } else { /* Assume IPv4 */
+ __iface_fill_net_state(net_param, iface, IFACE_TYPE_IPV4);
+ net_param++;
+ net_config->count++;
+
+ if (__iface_fill_net_ipv4_addr(net_param, iface,
+ ISCSI_NET_PARAM_IPV4_ADDR) == 0) {
+ net_param++;
+ net_config->count++;
+ }
+
+ if (__iface_fill_net_ipv4_addr(net_param, iface,
+ ISCSI_NET_PARAM_IPV4_SUBNET) == 0) {
+ net_param++;
+ net_config->count++;
+ }
+
+ if (__iface_fill_net_ipv4_addr(net_param, iface,
+ ISCSI_NET_PARAM_IPV4_GW) == 0) {
+ net_param++;
+ net_config->count++;
+ }
+
+ if (!state_set) {
+ __iface_fill_net_state(net_param, iface,
+ IFACE_TYPE_IPV4);
+ net_param++;
+ net_config->count++;
+ }
+ }
+ return 0;
+}
+
+/* Return total number of params */
+int iface_build_net_config(struct iface_rec *iface_primary, int iface_all,
+ char *cfg_data)
+{
+ int num_found = 0, rc;
+ struct iface_net_config net_config;
+
+ log_debug(8, "In iface_build_net_config\n");
+
+ net_config.primary = iface_primary;
+ net_config.cfg_data = cfg_data;
+ net_config.count = 0;
+
+ if (iface_all)
+ rc = iface_for_each_iface(&net_config, 0, &num_found,
+ __iface_build_net_config);
+ else
+ rc = __iface_build_net_config(&net_config, iface_primary);
+
+ log_debug(8, "iface_build_net_config: rc = %d, count = %d\n",
+ rc, net_config.count);
+ return net_config.count;
+}
diff --git a/usr/iface.h b/usr/iface.h
index 9f6d47e..5217fc0 100644
--- a/usr/iface.h
+++ b/usr/iface.h
@@ -54,6 +54,8 @@ extern int iface_setup_from_boot_context(struct iface_rec *iface,
struct boot_context *context);
extern int iface_create_ifaces_from_boot_contexts(struct list_head *ifaces,
struct list_head *targets);
+extern int iface_build_net_config(struct iface_rec *iface_primary,
+ int iface_all, char *cfg_data);
#define iface_fmt "[hw=%s,ip=%s,net_if=%s,iscsi_if=%s]"
#define iface_str(_iface) \
diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h
index 93b4917..dd4ce41 100644
--- a/usr/iscsi_ipc.h
+++ b/usr/iscsi_ipc.h
@@ -129,6 +129,9 @@ struct iscsi_ipc {
int (*recv_pdu_begin) (struct iscsi_conn *conn);
int (*recv_pdu_end) (struct iscsi_conn *conn);
+
+ int (*set_net_config) (uint64_t transport_handle, uint32_t host_no,
+ char *cfg_buf, uint32_t param_count);
};
#endif /* ISCSI_IPC_H */
diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c
index 3c8abd2..7a0a131 100644
--- a/usr/iscsiadm.c
+++ b/usr/iscsiadm.c
@@ -70,7 +70,9 @@ enum iscsiadm_op {
OP_DELETE = 0x2,
OP_UPDATE = 0x4,
OP_SHOW = 0x8,
- OP_NONPERSISTENT = 0x10
+ OP_NONPERSISTENT = 0x10,
+ OP_APPLY = 0x20,
+ OP_APPLY_ALL = 0x40
};
static struct option const long_options[] =
@@ -138,6 +140,10 @@ str_to_op(char *str)
op = OP_SHOW;
else if (!strcmp("nonpersistent", str))
op = OP_NONPERSISTENT;
+ else if (!strcmp("apply", str))
+ op = OP_APPLY;
+ else if (!strcmp("applyall", str))
+ op = OP_APPLY_ALL;
else
op = OP_NOOP;
@@ -1091,6 +1097,57 @@ static void catch_sigint( int signo ) {
exit(1);
}
+static int
+iface_apply_net_config(struct iface_rec *iface, int op)
+{
+ int rc;
+ iscsiadm_req_t *req = NULL;
+ iscsiadm_rsp_t rsp;
+ char *cfg_buf = NULL;
+ char *buf = NULL;
+ uint32_t host_no;
+ int param_count;
+ int iface_all = 0;
+
+ log_debug(8, "Calling iscsid, to apply net config for"
+ "iface.name = %s\n", iface->name);
+
+ buf = calloc((sizeof(iscsiadm_req_t) + (ISCSI_MAX_NET_PARAMS *
+ sizeof(struct iscsi_net_param))), sizeof(char));
+ if (!buf)
+ return ENOMEM;
+
+ if (op == OP_APPLY_ALL)
+ iface_all = 1;
+
+ req = (iscsiadm_req_t *)buf;
+ cfg_buf = buf + sizeof(iscsiadm_req_t);
+
+ /* For each iface, match hw address */
+ param_count = iface_build_net_config(iface, iface_all, cfg_buf);
+ if (!param_count) {
+ log_debug(8, "iface_apply_net_config: Nothing to configure\n");
+ goto free_buf;
+ }
+
+ host_no = iscsi_sysfs_get_host_no_from_hwinfo(iface, &rc);
+
+ req->command = MGMT_IPC_SET_HOST_NET_CONFIG;
+ strncpy(req->u.set_net_config.transport_name, iface->transport_name,
+ ISCSI_TRANSPORT_NAME_MAXLEN);
+ req->u.set_net_config.host_no = host_no;
+ req->payload_len = param_count * sizeof(struct iscsi_net_param);
+
+ rc = iscsid_exec_req((iscsiadm_req_t *)req, &rsp, 1);
+ log_debug(8, "iface_apply_net_config rc=%d\n", rc);
+
+free_buf:
+ free(buf);
+ if (rc)
+ return EIO;
+ return 0;
+}
+
/* TODO: merge iter helpers and clean them up, so we can use them here */
static int exec_iface_op(int op, int do_show, int info_level,
struct iface_rec *iface, char *name, char *value)
@@ -1217,6 +1274,27 @@ update_fail:
log_error("Could not update iface %s: %s",
iface->name, iscsi_err_to_str(rc));
break;
+ case OP_APPLY:
+ case OP_APPLY_ALL:
+ if (!iface) {
+ log_error("Apply requires iface.");
+ rc = EINVAL;
+ break;
+ }
+
+ rc = iface_conf_read(iface);
+ if (rc) {
+ log_error("Could not read iface %s (%d).",
+ iface->name, rc);
+ break;
+ }
+
+ /* For each iface where hwaddress match, start filling the
+ * buffer with iscsi_net_param
+ */
+ rc = iface_apply_net_config(iface, op);
+ printf("%s applied.\n", iface->name);
+ break;
default:
if (!iface || (iface && info_level > 0)) {
if (op == OP_NOOP || op == OP_SHOW)
diff --git a/usr/iscsid_req.c b/usr/iscsid_req.c
index a49b667..3c774af 100644
--- a/usr/iscsid_req.c
+++ b/usr/iscsid_req.c
@@ -102,7 +102,8 @@ int iscsid_request(int *fd, iscsiadm_req_t *req, int start_iscsid)
if (err)
return err;
- if ((err = write(*fd, req, sizeof(*req))) != sizeof(*req)) {
+ err = write(*fd, req, (sizeof(*req) + req->payload_len));
+ if (err != (sizeof(*req) + req->payload_len)) {
log_error("got write error (%d/%d) on cmd %d, daemon died?",
err, errno, req->command);
close(*fd);
diff --git a/usr/mgmt_ipc.c b/usr/mgmt_ipc.c
index 3e4d2ef..9218fc2 100644
--- a/usr/mgmt_ipc.c
+++ b/usr/mgmt_ipc.c
@@ -486,6 +486,28 @@ mgmt_ipc_read_req(queue_task_t *qtask)
return rc;
}
+static int
+mgmt_ipc_set_host_net_config(queue_task_t *qtask)
+{
+ struct iscsi_transport *t;
+ int param_count;
+ int err;
+
+ param_count = qtask->req.payload_len/sizeof(struct iscsi_net_param);
+
+ t = iscsi_sysfs_get_transport_by_name(
+ qtask->req.u.set_net_config.transport_name);
+
+ if (ipc->set_net_config(t->handle,
+ qtask->req.u.set_net_config.host_no, qtask->payload, param_count))
+ err = ISCSI_ERR;
+ else
+ err = ISCSI_SUCCESS;
+
+ mgmt_ipc_write_rsp(qtask, err);
+ return ISCSI_SUCCESS;
+}
+
static mgmt_ipc_fn_t * mgmt_ipc_functions[__MGMT_IPC_MAX_COMMAND] = {
[MGMT_IPC_SESSION_LOGIN] = mgmt_ipc_session_login,
[MGMT_IPC_SESSION_LOGOUT] = mgmt_ipc_session_logout,
@@ -503,6 +525,7 @@ static mgmt_ipc_fn_t * mgmt_ipc_functions[__MGMT_IPC_MAX_COMMAND] = {
[MGMT_IPC_NOTIFY_DEL_NODE] = mgmt_ipc_notify_del_node,
[MGMT_IPC_NOTIFY_ADD_PORTAL] = mgmt_ipc_notify_add_portal,
[MGMT_IPC_NOTIFY_DEL_PORTAL] = mgmt_ipc_notify_del_portal,
+[MGMT_IPC_SET_HOST_NET_CONFIG] = mgmt_ipc_set_host_net_config,
};
void mgmt_ipc_handle(int accept_fd)
diff --git a/usr/mgmt_ipc.h b/usr/mgmt_ipc.h
index 7d8ce72..e1c208a 100644
--- a/usr/mgmt_ipc.h
+++ b/usr/mgmt_ipc.h
@@ -46,6 +46,7 @@ typedef enum iscsiadm_cmd {
MGMT_IPC_NOTIFY_DEL_NODE = 17,
MGMT_IPC_NOTIFY_ADD_PORTAL = 18,
MGMT_IPC_NOTIFY_DEL_PORTAL = 19,
+ MGMT_IPC_SET_HOST_NET_CONFIG = 20,
__MGMT_IPC_MAX_COMMAND
} iscsiadm_cmd_e;
@@ -77,6 +78,10 @@ typedef struct iscsiadm_req {
char value[IFNAMSIZ + 1];
} set_host_param;
+ struct ipc_msg_set_net_config {
+ char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN];
+ uint32_t host_no;
+ } set_net_config;
} u;
} iscsiadm_req_t;
diff --git a/usr/netlink.c b/usr/netlink.c
index c5d8650..c54792b 100644
--- a/usr/netlink.c
+++ b/usr/netlink.c
@@ -53,8 +53,12 @@ static struct iscsi_ipc_ev_clbk *ipc_ev_clbk;
static int ctldev_handle(void);
+#define NLM_SETNETPARAM_DEFAULT_MAX \
+ (ISCSI_MAX_NET_PARAMS * sizeof(struct iscsi_net_param) + \
+ sizeof(struct iscsi_uevent))
+
#define NLM_BUF_DEFAULT_MAX \
- (NLMSG_SPACE(ISCSI_DEF_MAX_RECV_SEG_LEN + \
+ (NLMSG_SPACE(NLM_SETNETPARAM_DEFAULT_MAX + \
sizeof(struct iscsi_hdr)))
#define PDU_SENDBUF_DEFAULT_MAX \
@@ -255,19 +259,15 @@ kwritev(enum iscsi_uevent_e type, struct iovec *iovp, int count)
* cleanup. (Dima)
*/
static int
-__kipc_call(void *iov_base, int iov_len)
+__kipc_call(struct iovec *iovp, int count)
{
int rc, iferr;
- struct iovec iov;
- struct iscsi_uevent *ev = iov_base;
+ struct iscsi_uevent *ev = iovp[0].iov_base;
enum iscsi_uevent_e type = ev->type;
log_debug(7, "in %s", __FUNCTION__);
- iov.iov_base = iov_base;
- iov.iov_len = iov_len;
-
- rc = kwritev(type, &iov, 1);
+ rc = kwritev(type, iovp, count);
do {
if ((rc = nlpayload_read(ctrl_fd, (void*)ev,
@@ -327,6 +327,7 @@ ksendtargets(uint64_t transport_handle, uint32_t host_no, struct sockaddr *addr)
{
int rc, addrlen;
struct iscsi_uevent *ev;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -348,7 +349,9 @@ ksendtargets(uint64_t transport_handle, uint32_t host_no, struct sockaddr *addr)
}
memcpy(setparam_buf + sizeof(*ev), addr, addrlen);
- rc = __kipc_call(ev, sizeof(*ev) + addrlen);
+ iov.iov_base = ev;
+ iov.iov_len = sizeof(*ev) + addrlen;
+ rc = __kipc_call(&iov, 1);
if (rc < 0) {
log_error("sendtargets failed rc%d\n", rc);
return rc;
@@ -363,6 +366,7 @@ kcreate_session(uint64_t transport_handle, uint64_t ep_handle,
{
int rc;
struct iscsi_uevent ev;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -383,9 +387,11 @@ kcreate_session(uint64_t transport_handle, uint64_t ep_handle,
ev.u.c_bound_session.ep_handle = ep_handle;
}
- if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) {
+ iov.iov_base = &ev;
+ iov.iov_len = sizeof(ev);
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
- }
*hostno = ev.r.c_session_ret.host_no;
*out_sid = ev.r.c_session_ret.sid;
@@ -398,6 +404,7 @@ kdestroy_session(uint64_t transport_handle, uint32_t sid)
{
int rc;
struct iscsi_uevent ev;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -407,9 +414,11 @@ kdestroy_session(uint64_t transport_handle, uint32_t sid)
ev.transport_handle = transport_handle;
ev.u.d_session.sid = sid;
- if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) {
+ iov.iov_base = &ev;
+ iov.iov_len = sizeof(ev);
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
- }
return 0;
}
@@ -419,6 +428,7 @@ kunbind_session(uint64_t transport_handle, uint32_t sid)
{
int rc;
struct iscsi_uevent ev;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -428,9 +438,11 @@ kunbind_session(uint64_t transport_handle, uint32_t sid)
ev.transport_handle = transport_handle;
ev.u.d_session.sid = sid;
- if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) {
+ iov.iov_base = &ev;
+ iov.iov_len = sizeof(ev);
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
- }
return 0;
}
@@ -441,6 +453,7 @@ kcreate_conn(uint64_t transport_handle, uint32_t sid,
{
int rc;
struct iscsi_uevent ev;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -451,7 +464,10 @@ kcreate_conn(uint64_t transport_handle, uint32_t sid,
ev.u.c_conn.cid = cid;
ev.u.c_conn.sid = sid;
- if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) {
+ iov.iov_base = &ev;
+ iov.iov_len = sizeof(ev);
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0) {
log_debug(7, "returned %d", rc);
return rc;
}
@@ -468,6 +484,7 @@ kdestroy_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid)
{
int rc;
struct iscsi_uevent ev;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -478,9 +495,11 @@ kdestroy_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid)
ev.u.d_conn.sid = sid;
ev.u.d_conn.cid = cid;
- if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) {
+ iov.iov_base = &ev;
+ iov.iov_len = sizeof(ev);
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
- }
return 0;
}
@@ -491,6 +510,7 @@ kbind_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid,
{
int rc;
struct iscsi_uevent ev;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -503,9 +523,11 @@ kbind_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid,
ev.u.b_conn.transport_eph = transport_eph;
ev.u.b_conn.is_leading = is_leading;
- if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) {
+ iov.iov_base = &ev;
+ iov.iov_len = sizeof(ev);
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
- }
*retcode = ev.r.retcode;
@@ -564,7 +586,8 @@ ksend_pdu_end(uint64_t transport_handle, uint32_t sid, uint32_t cid,
iov.iov_base = xmitbuf;
iov.iov_len = xmitlen;
- if ((rc = __kipc_call(xmitbuf, xmitlen)) < 0)
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
goto err;
if (ev->r.retcode) {
*retcode = ev->r.retcode;
@@ -594,6 +617,7 @@ kset_host_param(uint64_t transport_handle, uint32_t host_no,
struct iscsi_uevent *ev;
char *param_str;
int rc, len;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -620,9 +644,11 @@ kset_host_param(uint64_t transport_handle, uint32_t host_no,
}
ev->u.set_host_param.len = len = strlen(param_str) + 1;
- if ((rc = __kipc_call(ev, sizeof(*ev) + len)) < 0) {
+ iov.iov_base = ev;
+ iov.iov_len = sizeof(*ev) + len;
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
- }
return 0;
}
@@ -634,6 +660,7 @@ kset_param(uint64_t transport_handle, uint32_t sid, uint32_t cid,
struct iscsi_uevent *ev;
char *param_str;
int rc, len;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -661,9 +688,11 @@ kset_param(uint64_t transport_handle, uint32_t sid, uint32_t cid,
}
ev->u.set_param.len = len = strlen(param_str) + 1;
- if ((rc = __kipc_call(ev, sizeof(*ev) + len)) < 0) {
+ iov.iov_base = ev;
+ iov.iov_len = sizeof(*ev) + len;
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
- }
return 0;
}
@@ -673,6 +702,7 @@ kstop_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid, int flag)
{
int rc;
struct iscsi_uevent ev;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -684,9 +714,11 @@ kstop_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid, int flag)
ev.u.stop_conn.cid = cid;
ev.u.stop_conn.flag = flag;
- if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) {
+ iov.iov_base = &ev;
+ iov.iov_len = sizeof(ev);
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
- }
return 0;
}
@@ -697,6 +729,7 @@ kstart_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid,
{
int rc;
struct iscsi_uevent ev;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -707,9 +740,11 @@ kstart_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid,
ev.u.start_conn.sid = sid;
ev.u.start_conn.cid = cid;
- if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) {
+ iov.iov_base = &ev;
+ iov.iov_len = sizeof(ev);
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
- }
*retcode = ev.r.retcode;
return 0;
@@ -774,6 +809,7 @@ ktransport_ep_connect(iscsi_conn_t *conn, int non_blocking)
int rc, addrlen;
struct iscsi_uevent *ev;
struct sockaddr *dst_addr = (struct sockaddr *)&conn->saddr;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -801,7 +837,10 @@ ktransport_ep_connect(iscsi_conn_t *conn, int non_blocking)
}
memcpy(setparam_buf + sizeof(*ev), dst_addr, addrlen);
- if ((rc = __kipc_call(ev, sizeof(*ev) + addrlen)) < 0)
+ iov.iov_base = ev;
+ iov.iov_len = sizeof(*ev) + addrlen;
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
if (!ev->r.ep_connect_ret.handle)
@@ -819,6 +858,7 @@ ktransport_ep_poll(iscsi_conn_t *conn, int timeout_ms)
{
int rc;
struct iscsi_uevent ev;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -829,7 +869,10 @@ ktransport_ep_poll(iscsi_conn_t *conn, int timeout_ms)
ev.u.ep_poll.ep_handle = conn->transport_ep_handle;
ev.u.ep_poll.timeout_ms = timeout_ms;
- if ((rc = __kipc_call(&ev, sizeof(ev))) < 0)
+ iov.iov_base = &ev;
+ iov.iov_len = sizeof(ev);
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
return ev.r.retcode;
@@ -840,6 +883,7 @@ ktransport_ep_disconnect(iscsi_conn_t *conn)
{
int rc;
struct iscsi_uevent ev;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -852,7 +896,10 @@ ktransport_ep_disconnect(iscsi_conn_t *conn)
ev.transport_handle = conn->session->t->handle;
ev.u.ep_disconnect.ep_handle = conn->transport_ep_handle;
- if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) {
+ iov.iov_base = &ev;
+ iov.iov_len = sizeof(ev);
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0) {
log_error("connnection %d:%d transport disconnect failed for "
"ep %" PRIu64 " with error %d.", conn->session->id,
conn->id, conn->transport_ep_handle, rc);
@@ -869,6 +916,7 @@ kget_stats(uint64_t transport_handle, uint32_t sid, uint32_t cid,
struct iscsi_uevent ev;
char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))];
struct nlmsghdr *nlh;
+ struct iovec iov;
log_debug(7, "in %s", __FUNCTION__);
@@ -879,9 +927,11 @@ kget_stats(uint64_t transport_handle, uint32_t sid, uint32_t cid,
ev.u.get_stats.sid = sid;
ev.u.get_stats.cid = cid;
- if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) {
+ iov.iov_base = &ev;
+ iov.iov_len = sizeof(ev);
+ rc = __kipc_call(&iov, 1);
+ if (rc < 0)
return rc;
- }
if ((rc = nl_read(ctrl_fd, nlm_ev,
NLMSG_SPACE(sizeof(struct iscsi_uevent)), MSG_PEEK)) < 0) {
@@ -907,6 +957,40 @@ kget_stats(uint64_t transport_handle, uint32_t sid, uint32_t cid,
return 0;
}
+static int
+kset_net_config(uint64_t transport_handle, uint32_t host_no,
+ char *cfg_buf, uint32_t param_count)
+{
+ struct iscsi_uevent ev;
+ int rc, ev_len, msg_length;
+ struct iovec iov[2];
+
+ log_debug(8, "in %s", __FUNCTION__);
+
+ ev_len = sizeof(ev);
+ ev.type = ISCSI_UEVENT_SET_NET_CONFIG;
+ ev.transport_handle = transport_handle;
+ ev.u.set_net_config.host_no = host_no;
+ ev.u.set_net_config.count = param_count;
+
+ msg_length = param_count * sizeof(struct iscsi_net_param);
+
+ if (msg_length > (NLM_SETNETPARAM_DEFAULT_MAX - ev_len)) {
+ log_error("invalid length %d\n", msg_length);
+ return -EINVAL;
+ }
+
+ iov[0].iov_base = &ev;
+ iov[0].iov_len = ev_len;
+ iov[1].iov_base = cfg_buf;
+ iov[1].iov_len = msg_length;
+ rc = __kipc_call(iov, 2);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
static void drop_data(struct nlmsghdr *nlh)
{
int ev_size;
@@ -1151,6 +1235,7 @@ struct iscsi_ipc nl_ipc = {
.read = kread,
.recv_pdu_begin = krecv_pdu_begin,
.recv_pdu_end = krecv_pdu_end,
+ .set_net_config = kset_net_config,
};
struct iscsi_ipc *ipc = &nl_ipc;
--
1.7.3.2
This message and any attached documents contain information from QLogic Corporation or its wholly-owned subsidiaries that may be confidential. If you are not the intended recipient, you may not read, copy, distribute, or use this information. If you have received this transmission in error, please notify the sender immediately by reply e-mail and then delete this message.
Some comments below.
On 02/25/2011 04:56 AM, Vikas Chaudhary wrote:
> struct iscsi_net_param {
> uint32_t param_type; /* enum iscsi_net_param */
> uint32_t iface_type; /* IPv4 or IPv6 */
> uint32_t iface_num; /* iface number, 0 - n */
> uint32_t length; /* Actual length of the param */
> uint32_t offset; /* For param with length> 256 */
I did not understand the offset value.
> uint8_t value[256]; /* max is 223 iscsi name */ } __attribute__((packed));
I think you can just dynamically allocate the value.
We did not do this for other events/params, because they are needed for
the reconnection. For the case where we could not allocate memory and
are trying to reconnect a sessions that the vm is trying to write pages
out to, then having the set value size made it simple to preallocate
param/event structs.
For setup there is not need to preallocate.
> } __attribute__((packed));
>
> 2.1.3 New parameters in iface
> Added following params:-
>
> typedef struct iface_rec {
> struct list_head list;
> /* iscsi iface record name */
> char name[ISCSI_MAX_IFACE_LEN];
> + char iface_num[ISCSI_MAX_STR_LEN];
What is the iface_num? Is that a unique id? If so what is
allocating/managing that iface number id space or what is it based on
then? Is the id/num also global or does each host have its own id space?
If it is just a integer then why the string definition?
> /* network layer iface name (eth0) */
> char netdev[IFNAMSIZ];
> char ipaddress[NI_MAXHOST];
> + char subnet_mask[NI_MAXHOST];
> + char gateway[NI_MAXHOST];
> + char bootproto[ISCSI_MAX_STR_LEN];
> + char ipv6_linklocal[NI_MAXHOST];
> + char ipv6_router[NI_MAXHOST];
> + char autocfg[NI_MAXHOST];
> + char vlan[ISCSI_MAX_STR_LEN];
> + char state[ISCSI_MAX_STR_LEN]; /* 0 = disable,
> + * 1 = enable */
Is this really just meant to be 0 or 1 or was it going to be a string
and have more values?
If length of param is greater than 256, then param will be split into two params with same type but different offset.
The value is split into two param structs. First param will have offset value zero and second param will have offset value as 256.
>
>> uint8_t value[256]; /* max is 223 iscsi name */ }
>__attribute__((packed));
>
>I think you can just dynamically allocate the value.
>
>We did not do this for other events/params, because they are needed for
>the reconnection. For the case where we could not allocate memory and
>are trying to reconnect a sessions that the vm is trying to write pages
>out to, then having the set value size made it simple to preallocate
>param/event structs.
>
>For setup there is not need to preallocate.
>
We use pre-allocated memory as we need to send all network parameters to the driver as single data blob.
We can modify code to use dynamically allocated memory as suggested but it will add complexity in the code.
If we use dynamic memory allocation, we need to keep track of the location where each parameter and its corresponding data starts.
The number of network parameters to be send can change each time based on how user configures iface. Therefore it becomes complex to identify
how much memory needs to be allocated each time.
For example:
Suppose we have following two iface files:
IFACE A:
# BEGIN RECORD 2.0-872
iface.iscsi_ifacename = qla4xxx.00:0e:1e:04:93:92
iface.ipaddress = 192.168.1.71
iface.subnet_mask = 255.255.255.0
iface.gateway = 192.168.1.1
iface.hwaddress = 00:0e:1e:04:93:92
iface.transport_name = qla4xxx
iface.state = enable
# END RECORD
IFACE B:
# BEGIN RECORD 2.0-872
iface.iscsi_ifacename = qla4xxx.00:0e:1e:04:93:92
iface.ipaddress = 1234::5678
iface.hwaddress = 00:0e:1e:04:93:92
iface.transport_name = qla4xxx
iface.state = enable
# END RECORD
In IFACE A, we require 4 bytes for ipv4 address whereas in IFACE B, we require 16 bytes for ipv16 address value.
As the number of parameters go on increasing, we need to find out memory requirement for them as well.
As we need to send complete netconfig data as a single blob, first we need to find out number of valid parameters
in the iface which are required to be sent to the driver. Depending on the parameter, we need to find out memory requirement of each parameter.
This requires iface files to be parsed twice, one to find out memory requirement and other to fill up the data in the allocated memory.
This additional processing is required for dynamic memory allocation.
Currently we assume a fixed amount of memory which can accommodate max params defined as 20 per interface * 5 interface per host.
This allows traversing the iface record once and fill up to the max supported params per interface.
Please let us know your thoughts about should we continue using pre-allocated memory or dynamically allocated memory.
>
>> } __attribute__((packed));
>>
>> 2.1.3 New parameters in iface
>> Added following params:-
>>
>> typedef struct iface_rec {
>> struct list_head list;
>> /* iscsi iface record name */
>> char name[ISCSI_MAX_IFACE_LEN];
>> + char iface_num[ISCSI_MAX_STR_LEN];
>
>
>What is the iface_num? Is that a unique id? If so what is
>allocating/managing that iface number id space or what is it based on
>then? Is the id/num also global or does each host have its own id space?
>
The adapter port can have multiple interfaces. For example qla4xxx adapter supports 1 IPv4 and 2 IPv6 addresses. The iface_num will indicate which iface to use.
By default if user does not specify the iface_num then its value is 0 indicating the first interface. For IPv6 if user wants to configure both IPv6 addresses then user must specify the iface_num = 0 (first IPv6 interface) and iface_num = 1 (second IPv6 interface) while configuring the ifaces. Similarly if an adapter supports multiple IPv4 address then user must specify iface_num = 0 for first IPv4 interface and iface_num = 1 for next and so on. The iface_num space is separate for IPv4 and IPv6.
Based on the iface_num driver also decides for which interface these parameter values are.
>If it is just a integer then why the string definition?
>
It should be integer. Will change it to type integer.
>> /* network layer iface name (eth0) */
>> char netdev[IFNAMSIZ];
>> char ipaddress[NI_MAXHOST];
>> + char subnet_mask[NI_MAXHOST];
>> + char gateway[NI_MAXHOST];
>> + char bootproto[ISCSI_MAX_STR_LEN];
>> + char ipv6_linklocal[NI_MAXHOST];
>> + char ipv6_router[NI_MAXHOST];
>> + char autocfg[NI_MAXHOST];
>> + char vlan[ISCSI_MAX_STR_LEN];
>> + char state[ISCSI_MAX_STR_LEN]; /* 0 = disable,
>> + * 1 = enable
>*/
>
>Is this really just meant to be 0 or 1 or was it going to be a string
>and have more values?
Its string, user can specify "enable" / "disable" , we will change the comment.
Are you having iscsid do the actual set_net_config netlink msg because
that is how we were doing other stuff at the time or was there another
reason? I ask because we used to do all nl msgs through iscsid, but
there is no need to do this now. You can just do:
int fd;
fd = ipc->ctldev_open();
if (fd < 0)
return some_error;
ipc->writev(....)
ipc->ctldev_close();
It is nice because you do not have to go through iscsid just to send a msg.
It does not see very friendly. Do you have any params using this in the
patches sent?
In your patch though you read in the iface into memory. If you did
variable length params, you still would only need to read it once, but
you would need to loop over the list of params twice.
>
> Currently we assume a fixed amount of memory which can accommodate max params defined as 20 per interface * 5 interface per host.
> This allows traversing the iface record once and fill up to the max supported params per interface.
>
> Please let us know your thoughts about should we continue using pre-allocated memory or dynamically allocated memory.
I think you can do variable sized params, and just loop over the params
once by doing a multi iovec sendmsg. I think maybe long ago the kernel
did not support multiple iovecs in netlink, but i think now you could do
something like:
__fill_some_param(struct iovec *iov)
{
int len;
len = sizeof(struct iscsi_net_param) + sizof(net_param->length);
iov->iov_base = malloc(len);
iov->iov_len = len;
.......
}
__iface_build_net_config(nl_buf)
{
iovs = iscsi_nl_buf_get_iovs(nl_buf);
__fill_some_param(iovs[0])
__fill_some_other_param(iovs[1])
}
netlink.c
struct iscsi_nl_buf {
int nr_iovs;
struct iovec *iovs;
}
iscsi_nl_allocate_buf(int num_params)
{
struct iscsi_nl_buf buf;
/* +2 for the event and the nlmsghdr */
nl_buf = malloc(sizeof(struct iscsi_nl_buf) + ((num_params + 2) *
sizeof(*iov)))
nl_buf->iovs = nl_buf + 1;
......
return nl_buf;
}
struct iovec *iscsi_nl_get_iovs(nl_buf)
{
/* start at 2, because 0 is for nlmsghdr and 1 is for event */
return nl_buf->iovs + 2;
}
set_net_config(nl_buf)
{
struct iscsi_event ev;
ev.X = Y;
........
nl_buf->iovs[1].iov_base = &ev;
nl_buf->iovs[1].iov_len = sizeof(ev);
__kipc_call(nl_buf);
}
kwritev(nl_buf)
{
struct nlmsghdr nlh;
nlh.X = Y;
........
nl_buf->iovs[0].iov_base = &nlh;
/* remove the memcpy to NLMSG_DATA and send iovec */
.....
msg.msg_iov.iov = nl_buf->iovs;
.....
}
/* convert other code */
We used iscsid since other stuff was done in that fasion...next RFC/patch we will change to send net_config using iscsiadm.
Thanks for the feedback.
We are working on the multiple iovecs suggestion.
Thanks
Lalit.
Let me know if it is not working well. I am open to suggestions.