diff options
Diffstat (limited to 'drivers/s390/net/netiucv.c')
| -rw-r--r-- | drivers/s390/net/netiucv.c | 293 |
1 files changed, 153 insertions, 140 deletions
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 65ebee0a326..ce16d1bdb20 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -63,6 +63,7 @@ #include <asm/io.h> #include <asm/uaccess.h> +#include <asm/ebcdic.h> #include <net/iucv/iucv.h> #include "fsm.h" @@ -75,7 +76,7 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver"); * Debug Facility stuff */ #define IUCV_DBF_SETUP_NAME "iucv_setup" -#define IUCV_DBF_SETUP_LEN 32 +#define IUCV_DBF_SETUP_LEN 64 #define IUCV_DBF_SETUP_PAGES 2 #define IUCV_DBF_SETUP_NR_AREAS 1 #define IUCV_DBF_SETUP_LEVEL 3 @@ -104,15 +105,9 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver"); DECLARE_PER_CPU(char[256], iucv_dbf_txt_buf); -/* Allow to sort out low debug levels early to avoid wasted sprints */ -static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level) -{ - return (level <= dbf_grp->level); -} - #define IUCV_DBF_TEXT_(name, level, text...) \ do { \ - if (iucv_dbf_passes(iucv_dbf_##name, level)) { \ + if (debug_level_enabled(iucv_dbf_##name, level)) { \ char* __buf = get_cpu_var(iucv_dbf_txt_buf); \ sprintf(__buf, text); \ debug_text_event(iucv_dbf_##name, level, __buf); \ @@ -129,26 +124,6 @@ static inline int iucv_dbf_passes(debug_info_t *dbf_grp, int level) /** * some more debug stuff */ -#define IUCV_HEXDUMP16(importance,header,ptr) \ -PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \ - "%02x %02x %02x %02x %02x %02x %02x %02x\n", \ - *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \ - *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \ - *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \ - *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \ - *(((char*)ptr)+12),*(((char*)ptr)+13), \ - *(((char*)ptr)+14),*(((char*)ptr)+15)); \ -PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \ - "%02x %02x %02x %02x %02x %02x %02x %02x\n", \ - *(((char*)ptr)+16),*(((char*)ptr)+17), \ - *(((char*)ptr)+18),*(((char*)ptr)+19), \ - *(((char*)ptr)+20),*(((char*)ptr)+21), \ - *(((char*)ptr)+22),*(((char*)ptr)+23), \ - *(((char*)ptr)+24),*(((char*)ptr)+25), \ - *(((char*)ptr)+26),*(((char*)ptr)+27), \ - *(((char*)ptr)+28),*(((char*)ptr)+29), \ - *(((char*)ptr)+30),*(((char*)ptr)+31)); - #define PRINTK_HEADER " iucv: " /* for debugging */ /* dummy device to make sure netiucv_pm functions are called */ @@ -226,6 +201,7 @@ struct iucv_connection { struct net_device *netdev; struct connection_profile prof; char userid[9]; + char userdata[17]; }; /** @@ -263,7 +239,7 @@ struct ll_header { }; #define NETIUCV_HDRLEN (sizeof(struct ll_header)) -#define NETIUCV_BUFSIZE_MAX 32768 +#define NETIUCV_BUFSIZE_MAX 65537 #define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX #define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN) #define NETIUCV_MTU_DEFAULT 9216 @@ -288,7 +264,12 @@ static inline int netiucv_test_and_set_busy(struct net_device *dev) return test_and_set_bit(0, &priv->tbusy); } -static u8 iucvMagic[16] = { +static u8 iucvMagic_ascii[16] = { + 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 +}; + +static u8 iucvMagic_ebcdic[16] = { 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 }; @@ -301,18 +282,38 @@ static u8 iucvMagic[16] = { * * @returns The printable string (static data!!) */ -static char *netiucv_printname(char *name) +static char *netiucv_printname(char *name, int len) { - static char tmp[9]; + static char tmp[17]; char *p = tmp; - memcpy(tmp, name, 8); - tmp[8] = '\0'; - while (*p && (!isspace(*p))) + memcpy(tmp, name, len); + tmp[len] = '\0'; + while (*p && ((p - tmp) < len) && (!isspace(*p))) p++; *p = '\0'; return tmp; } +static char *netiucv_printuser(struct iucv_connection *conn) +{ + static char tmp_uid[9]; + static char tmp_udat[17]; + static char buf[100]; + + if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) { + tmp_uid[8] = '\0'; + tmp_udat[16] = '\0'; + memcpy(tmp_uid, conn->userid, 8); + memcpy(tmp_uid, netiucv_printname(tmp_uid, 8), 8); + memcpy(tmp_udat, conn->userdata, 16); + EBCASC(tmp_udat, 16); + memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16); + sprintf(buf, "%s.%s", tmp_uid, tmp_udat); + return buf; + } else + return netiucv_printname(conn->userid, 8); +} + /** * States of the interface statemachine. */ @@ -563,15 +564,18 @@ static int netiucv_callback_connreq(struct iucv_path *path, { struct iucv_connection *conn = path->private; struct iucv_event ev; + static char tmp_user[9]; + static char tmp_udat[17]; int rc; - if (memcmp(iucvMagic, ipuser, sizeof(ipuser))) - /* ipuser must match iucvMagic. */ - return -EINVAL; rc = -EINVAL; + memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8); + memcpy(tmp_udat, ipuser, 16); + EBCASC(tmp_udat, 16); read_lock_bh(&iucv_connection_rwlock); list_for_each_entry(conn, &iucv_connection_list, list) { - if (strncmp(ipvmid, conn->userid, 8)) + if (strncmp(ipvmid, conn->userid, 8) || + strncmp(ipuser, conn->userdata, 16)) continue; /* Found a matching connection for this path. */ conn->path = path; @@ -580,6 +584,8 @@ static int netiucv_callback_connreq(struct iucv_path *path, fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev); rc = 0; } + IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n", + tmp_user, netiucv_printname(tmp_udat, 16)); read_unlock_bh(&iucv_connection_rwlock); return rc; } @@ -733,8 +739,12 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg) IUCV_DBF_TEXT(trace, 4, __func__); - if (conn && conn->netdev) - privptr = netdev_priv(conn->netdev); + if (!conn || !conn->netdev) { + IUCV_DBF_TEXT(data, 2, + "Send confirmation for unlinked connection\n"); + return; + } + privptr = netdev_priv(conn->netdev); conn->prof.tx_pending--; if (single_flag) { if ((skb = skb_dequeue(&conn->commit_queue))) { @@ -816,7 +826,7 @@ static void conn_action_connaccept(fsm_instance *fi, int event, void *arg) conn->path = path; path->msglim = NETIUCV_QUEUELEN_DEFAULT; path->flags = 0; - rc = iucv_path_accept(path, &netiucv_handler, NULL, conn); + rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn); if (rc) { IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc); return; @@ -854,7 +864,7 @@ static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg) IUCV_DBF_TEXT(trace, 3, __func__); fsm_deltimer(&conn->timer); - iucv_path_sever(conn->path, NULL); + iucv_path_sever(conn->path, conn->userdata); fsm_newstate(fi, CONN_STATE_STARTWAIT); } @@ -867,9 +877,9 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg) IUCV_DBF_TEXT(trace, 3, __func__); fsm_deltimer(&conn->timer); - iucv_path_sever(conn->path, NULL); - dev_info(privptr->dev, "The peer interface of the IUCV device" - " has closed the connection\n"); + iucv_path_sever(conn->path, conn->userdata); + dev_info(privptr->dev, "The peer z/VM guest %s has closed the " + "connection\n", netiucv_printuser(conn)); IUCV_DBF_TEXT(data, 2, "conn_action_connsever: Remote dropped connection\n"); fsm_newstate(fi, CONN_STATE_STARTWAIT); @@ -886,8 +896,6 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg) IUCV_DBF_TEXT(trace, 3, __func__); fsm_newstate(fi, CONN_STATE_STARTWAIT); - IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n", - netdev->name, conn->userid); /* * We must set the state before calling iucv_connect because the @@ -897,8 +905,11 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg) fsm_newstate(fi, CONN_STATE_SETUPWAIT); conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL); + IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n", + netdev->name, netiucv_printuser(conn)); + rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid, - NULL, iucvMagic, conn); + NULL, conn->userdata, conn); switch (rc) { case 0: netdev->tx_queue_len = conn->path->msglim; @@ -908,13 +919,13 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg) case 11: dev_warn(privptr->dev, "The IUCV device failed to connect to z/VM guest %s\n", - netiucv_printname(conn->userid)); + netiucv_printname(conn->userid, 8)); fsm_newstate(fi, CONN_STATE_STARTWAIT); break; case 12: dev_warn(privptr->dev, "The IUCV device failed to connect to the peer on z/VM" - " guest %s\n", netiucv_printname(conn->userid)); + " guest %s\n", netiucv_printname(conn->userid, 8)); fsm_newstate(fi, CONN_STATE_STARTWAIT); break; case 13: @@ -927,7 +938,7 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg) dev_err(privptr->dev, "z/VM guest %s has too many IUCV connections" " to connect with the IUCV device\n", - netiucv_printname(conn->userid)); + netiucv_printname(conn->userid, 8)); fsm_newstate(fi, CONN_STATE_CONNERR); break; case 15: @@ -972,7 +983,7 @@ static void conn_action_stop(fsm_instance *fi, int event, void *arg) netiucv_purge_skb_queue(&conn->collect_queue); if (conn->path) { IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n"); - iucv_path_sever(conn->path, iucvMagic); + iucv_path_sever(conn->path, conn->userdata); kfree(conn->path); conn->path = NULL; } @@ -1090,7 +1101,8 @@ dev_action_connup(fsm_instance *fi, int event, void *arg) fsm_newstate(fi, DEV_STATE_RUNNING); dev_info(privptr->dev, "The IUCV device has been connected" - " successfully to %s\n", privptr->conn->userid); + " successfully to %s\n", + netiucv_printuser(privptr->conn)); IUCV_DBF_TEXT(setup, 3, "connection is up and running\n"); break; @@ -1452,45 +1464,72 @@ static ssize_t user_show(struct device *dev, struct device_attribute *attr, struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); - return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid)); + return sprintf(buf, "%s\n", netiucv_printuser(priv->conn)); } -static ssize_t user_write(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int netiucv_check_user(const char *buf, size_t count, char *username, + char *userdata) { - struct netiucv_priv *priv = dev_get_drvdata(dev); - struct net_device *ndev = priv->conn->netdev; - char *p; - char *tmp; - char username[9]; - int i; - struct iucv_connection *cp; + const char *p; + int i; - IUCV_DBF_TEXT(trace, 3, __func__); - if (count > 9) { - IUCV_DBF_TEXT_(setup, 2, - "%d is length of username\n", (int) count); + p = strchr(buf, '.'); + if ((p && ((count > 26) || + ((p - buf) > 8) || + (buf + count - p > 18))) || + (!p && (count > 9))) { + IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n"); return -EINVAL; } - tmp = strsep((char **) &buf, "\n"); - for (i = 0, p = tmp; i < 8 && *p; i++, p++) { - if (isalnum(*p) || (*p == '$')) { - username[i]= toupper(*p); + for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) { + if (isalnum(*p) || *p == '$') { + username[i] = toupper(*p); continue; } - if (*p == '\n') { + if (*p == '\n') /* trailing lf, grr */ break; - } IUCV_DBF_TEXT_(setup, 2, - "username: invalid character %c\n", *p); + "conn_write: invalid character %02x\n", *p); return -EINVAL; } while (i < 8) username[i++] = ' '; username[8] = '\0'; + if (*p == '.') { + p++; + for (i = 0; i < 16 && *p; i++, p++) { + if (*p == '\n') + break; + userdata[i] = toupper(*p); + } + while (i > 0 && i < 16) + userdata[i++] = ' '; + } else + memcpy(userdata, iucvMagic_ascii, 16); + userdata[16] = '\0'; + ASCEBC(userdata, 16); + + return 0; +} + +static ssize_t user_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct netiucv_priv *priv = dev_get_drvdata(dev); + struct net_device *ndev = priv->conn->netdev; + char username[9]; + char userdata[17]; + int rc; + struct iucv_connection *cp; + + IUCV_DBF_TEXT(trace, 3, __func__); + rc = netiucv_check_user(buf, count, username, userdata); + if (rc) + return rc; + if (memcmp(username, priv->conn->userid, 9) && (ndev->flags & (IFF_UP | IFF_RUNNING))) { /* username changed while the interface is active. */ @@ -1499,15 +1538,17 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr, } read_lock_bh(&iucv_connection_rwlock); list_for_each_entry(cp, &iucv_connection_list, list) { - if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) { + if (!strncmp(username, cp->userid, 9) && + !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) { read_unlock_bh(&iucv_connection_rwlock); - IUCV_DBF_TEXT_(setup, 2, "user_write: Connection " - "to %s already exists\n", username); + IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s " + "already exists\n", netiucv_printuser(cp)); return -EEXIST; } } read_unlock_bh(&iucv_connection_rwlock); memcpy(priv->conn->userid, username, 9); + memcpy(priv->conn->userdata, userdata, 17); return count; } @@ -1537,7 +1578,8 @@ static ssize_t buffer_write (struct device *dev, struct device_attribute *attr, bs1 = simple_strtoul(buf, &e, 0); if (e && (!isspace(*e))) { - IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e); + IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %02x\n", + *e); return -EINVAL; } if (bs1 > NETIUCV_BUFSIZE_MAX) { @@ -1790,26 +1832,11 @@ static struct attribute_group netiucv_stat_attr_group = { .attrs = netiucv_stat_attrs, }; -static int netiucv_add_files(struct device *dev) -{ - int ret; - - IUCV_DBF_TEXT(trace, 3, __func__); - ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group); - if (ret) - return ret; - ret = sysfs_create_group(&dev->kobj, &netiucv_stat_attr_group); - if (ret) - sysfs_remove_group(&dev->kobj, &netiucv_attr_group); - return ret; -} - -static void netiucv_remove_files(struct device *dev) -{ - IUCV_DBF_TEXT(trace, 3, __func__); - sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group); - sysfs_remove_group(&dev->kobj, &netiucv_attr_group); -} +static const struct attribute_group *netiucv_attr_groups[] = { + &netiucv_stat_attr_group, + &netiucv_attr_group, + NULL, +}; static int netiucv_register_device(struct net_device *ndev) { @@ -1823,6 +1850,7 @@ static int netiucv_register_device(struct net_device *ndev) dev_set_name(dev, "net%s", ndev->name); dev->bus = &iucv_bus; dev->parent = iucv_root; + dev->groups = netiucv_attr_groups; /* * The release function could be called after the * module has been unloaded. It's _only_ task is to @@ -1840,22 +1868,14 @@ static int netiucv_register_device(struct net_device *ndev) put_device(dev); return ret; } - ret = netiucv_add_files(dev); - if (ret) - goto out_unreg; priv->dev = dev; dev_set_drvdata(dev, priv); return 0; - -out_unreg: - device_unregister(dev); - return ret; } static void netiucv_unregister_device(struct device *dev) { IUCV_DBF_TEXT(trace, 3, __func__); - netiucv_remove_files(dev); device_unregister(dev); } @@ -1864,7 +1884,8 @@ static void netiucv_unregister_device(struct device *dev) * Add it to the list of netiucv connections; */ static struct iucv_connection *netiucv_new_connection(struct net_device *dev, - char *username) + char *username, + char *userdata) { struct iucv_connection *conn; @@ -1893,6 +1914,8 @@ static struct iucv_connection *netiucv_new_connection(struct net_device *dev, fsm_settimer(conn->fsm, &conn->timer); fsm_newstate(conn->fsm, CONN_STATE_INVALID); + if (userdata) + memcpy(conn->userdata, userdata, 17); if (username) { memcpy(conn->userid, username, 9); fsm_newstate(conn->fsm, CONN_STATE_STOPPED); @@ -1919,6 +1942,7 @@ out: */ static void netiucv_remove_connection(struct iucv_connection *conn) { + IUCV_DBF_TEXT(trace, 3, __func__); write_lock_bh(&iucv_connection_rwlock); list_del_init(&conn->list); @@ -1926,7 +1950,7 @@ static void netiucv_remove_connection(struct iucv_connection *conn) fsm_deltimer(&conn->timer); netiucv_purge_skb_queue(&conn->collect_queue); if (conn->path) { - iucv_path_sever(conn->path, iucvMagic); + iucv_path_sever(conn->path, conn->userdata); kfree(conn->path); conn->path = NULL; } @@ -1985,7 +2009,7 @@ static void netiucv_setup_netdevice(struct net_device *dev) /** * Allocate and initialize everything of a net device. */ -static struct net_device *netiucv_init_netdevice(char *username) +static struct net_device *netiucv_init_netdevice(char *username, char *userdata) { struct netiucv_priv *privptr; struct net_device *dev; @@ -1994,6 +2018,7 @@ static struct net_device *netiucv_init_netdevice(char *username) netiucv_setup_netdevice); if (!dev) return NULL; + rtnl_lock(); if (dev_alloc_name(dev, dev->name) < 0) goto out_netdev; @@ -2004,7 +2029,7 @@ static struct net_device *netiucv_init_netdevice(char *username) if (!privptr->fsm) goto out_netdev; - privptr->conn = netiucv_new_connection(dev, username); + privptr->conn = netiucv_new_connection(dev, username, userdata); if (!privptr->conn) { IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n"); goto out_fsm; @@ -2015,6 +2040,7 @@ static struct net_device *netiucv_init_netdevice(char *username) out_fsm: kfree_fsm(privptr->fsm); out_netdev: + rtnl_unlock(); free_netdev(dev); return NULL; } @@ -2022,47 +2048,31 @@ out_netdev: static ssize_t conn_write(struct device_driver *drv, const char *buf, size_t count) { - const char *p; char username[9]; - int i, rc; + char userdata[17]; + int rc; struct net_device *dev; struct netiucv_priv *priv; struct iucv_connection *cp; IUCV_DBF_TEXT(trace, 3, __func__); - if (count>9) { - IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n"); - return -EINVAL; - } - - for (i = 0, p = buf; i < 8 && *p; i++, p++) { - if (isalnum(*p) || *p == '$') { - username[i] = toupper(*p); - continue; - } - if (*p == '\n') - /* trailing lf, grr */ - break; - IUCV_DBF_TEXT_(setup, 2, - "conn_write: invalid character %c\n", *p); - return -EINVAL; - } - while (i < 8) - username[i++] = ' '; - username[8] = '\0'; + rc = netiucv_check_user(buf, count, username, userdata); + if (rc) + return rc; read_lock_bh(&iucv_connection_rwlock); list_for_each_entry(cp, &iucv_connection_list, list) { - if (!strncmp(username, cp->userid, 9)) { + if (!strncmp(username, cp->userid, 9) && + !strncmp(userdata, cp->userdata, 17)) { read_unlock_bh(&iucv_connection_rwlock); - IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection " - "to %s already exists\n", username); + IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s " + "already exists\n", netiucv_printuser(cp)); return -EEXIST; } } read_unlock_bh(&iucv_connection_rwlock); - dev = netiucv_init_netdevice(username); + dev = netiucv_init_netdevice(username, userdata); if (!dev) { IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n"); return -ENODEV; @@ -2070,6 +2080,7 @@ static ssize_t conn_write(struct device_driver *drv, rc = netiucv_register_device(dev); if (rc) { + rtnl_unlock(); IUCV_DBF_TEXT_(setup, 2, "ret %d from netiucv_register_device\n", rc); goto out_free_ndev; @@ -2079,12 +2090,14 @@ static ssize_t conn_write(struct device_driver *drv, priv = netdev_priv(dev); SET_NETDEV_DEV(dev, priv->dev); - rc = register_netdev(dev); + rc = register_netdevice(dev); + rtnl_unlock(); if (rc) goto out_unreg; - dev_info(priv->dev, "The IUCV interface to %s has been" - " established successfully\n", netiucv_printname(username)); + dev_info(priv->dev, "The IUCV interface to %s has been established " + "successfully\n", + netiucv_printuser(priv->conn)); return count; |
