Commit 16a83b30 authored by Frank Pavlic's avatar Frank Pavlic Committed by Jeff Garzik

[PATCH] s390: netiucv driver fixes

[PATCH 2/9] s390: netiucv driver fixes

From: Frank Pavlic <fpavlic@de.ibm.com>
	- missing lock initialization added
        - avoid duplicate iucv-interfaces to the same peer
       	- rw-lock added for manipulating the list of
          defined iucv connections
Signed-off-by: default avatarFrank Pavlic <fpavlic@de.ibm.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 4c7ae6ea
...@@ -112,7 +112,12 @@ struct iucv_connection { ...@@ -112,7 +112,12 @@ struct iucv_connection {
/** /**
* Linked list of all connection structs. * Linked list of all connection structs.
*/ */
static struct iucv_connection *iucv_connections; struct iucv_connection_struct {
struct iucv_connection *iucv_connections;
rwlock_t iucv_rwlock;
};
static struct iucv_connection_struct iucv_conns;
/** /**
* Representation of event-data for the * Representation of event-data for the
...@@ -1368,8 +1373,10 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf, ...@@ -1368,8 +1373,10 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf,
struct net_device *ndev = priv->conn->netdev; struct net_device *ndev = priv->conn->netdev;
char *p; char *p;
char *tmp; char *tmp;
char username[10]; char username[9];
int i; int i;
struct iucv_connection **clist = &iucv_conns.iucv_connections;
unsigned long flags;
IUCV_DBF_TEXT(trace, 3, __FUNCTION__); IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
if (count>9) { if (count>9) {
...@@ -1382,7 +1389,7 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf, ...@@ -1382,7 +1389,7 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf,
tmp = strsep((char **) &buf, "\n"); tmp = strsep((char **) &buf, "\n");
for (i=0, p=tmp; i<8 && *p; i++, p++) { for (i=0, p=tmp; i<8 && *p; i++, p++) {
if (isalnum(*p) || (*p == '$')) if (isalnum(*p) || (*p == '$'))
username[i]= *p; username[i]= toupper(*p);
else if (*p == '\n') { else if (*p == '\n') {
/* trailing lf, grr */ /* trailing lf, grr */
break; break;
...@@ -1395,11 +1402,11 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf, ...@@ -1395,11 +1402,11 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf,
return -EINVAL; return -EINVAL;
} }
} }
while (i<9) while (i<8)
username[i++] = ' '; username[i++] = ' ';
username[9] = '\0'; username[8] = '\0';
if (memcmp(username, priv->conn->userid, 8)) { if (memcmp(username, priv->conn->userid, 9)) {
/* username changed */ /* username changed */
if (ndev->flags & (IFF_UP | IFF_RUNNING)) { if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
PRINT_WARN( PRINT_WARN(
...@@ -1410,6 +1417,19 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf, ...@@ -1410,6 +1417,19 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf,
return -EBUSY; return -EBUSY;
} }
} }
read_lock_irqsave(&iucv_conns.iucv_rwlock, flags);
while (*clist) {
if (!strncmp(username, (*clist)->userid, 9) ||
((*clist)->netdev != ndev))
break;
clist = &((*clist)->next);
}
read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
if (*clist) {
PRINT_WARN("netiucv: Connection to %s already exists\n",
username);
return -EEXIST;
}
memcpy(priv->conn->userid, username, 9); memcpy(priv->conn->userid, username, 9);
return count; return count;
...@@ -1781,13 +1801,15 @@ netiucv_unregister_device(struct device *dev) ...@@ -1781,13 +1801,15 @@ netiucv_unregister_device(struct device *dev)
static struct iucv_connection * static struct iucv_connection *
netiucv_new_connection(struct net_device *dev, char *username) netiucv_new_connection(struct net_device *dev, char *username)
{ {
struct iucv_connection **clist = &iucv_connections; unsigned long flags;
struct iucv_connection **clist = &iucv_conns.iucv_connections;
struct iucv_connection *conn = struct iucv_connection *conn =
kzalloc(sizeof(struct iucv_connection), GFP_KERNEL); kzalloc(sizeof(struct iucv_connection), GFP_KERNEL);
if (conn) { if (conn) {
skb_queue_head_init(&conn->collect_queue); skb_queue_head_init(&conn->collect_queue);
skb_queue_head_init(&conn->commit_queue); skb_queue_head_init(&conn->commit_queue);
spin_lock_init(&conn->collect_lock);
conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT; conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT;
conn->netdev = dev; conn->netdev = dev;
...@@ -1822,8 +1844,10 @@ netiucv_new_connection(struct net_device *dev, char *username) ...@@ -1822,8 +1844,10 @@ netiucv_new_connection(struct net_device *dev, char *username)
fsm_newstate(conn->fsm, CONN_STATE_STOPPED); fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
} }
write_lock_irqsave(&iucv_conns.iucv_rwlock, flags);
conn->next = *clist; conn->next = *clist;
*clist = conn; *clist = conn;
write_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
} }
return conn; return conn;
} }
...@@ -1835,14 +1859,17 @@ netiucv_new_connection(struct net_device *dev, char *username) ...@@ -1835,14 +1859,17 @@ netiucv_new_connection(struct net_device *dev, char *username)
static void static void
netiucv_remove_connection(struct iucv_connection *conn) netiucv_remove_connection(struct iucv_connection *conn)
{ {
struct iucv_connection **clist = &iucv_connections; struct iucv_connection **clist = &iucv_conns.iucv_connections;
unsigned long flags;
IUCV_DBF_TEXT(trace, 3, __FUNCTION__); IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
if (conn == NULL) if (conn == NULL)
return; return;
write_lock_irqsave(&iucv_conns.iucv_rwlock, flags);
while (*clist) { while (*clist) {
if (*clist == conn) { if (*clist == conn) {
*clist = conn->next; *clist = conn->next;
write_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
if (conn->handle) { if (conn->handle) {
iucv_unregister_program(conn->handle); iucv_unregister_program(conn->handle);
conn->handle = NULL; conn->handle = NULL;
...@@ -1855,6 +1882,7 @@ netiucv_remove_connection(struct iucv_connection *conn) ...@@ -1855,6 +1882,7 @@ netiucv_remove_connection(struct iucv_connection *conn)
} }
clist = &((*clist)->next); clist = &((*clist)->next);
} }
write_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
} }
/** /**
...@@ -1947,9 +1975,11 @@ static ssize_t ...@@ -1947,9 +1975,11 @@ static ssize_t
conn_write(struct device_driver *drv, const char *buf, size_t count) conn_write(struct device_driver *drv, const char *buf, size_t count)
{ {
char *p; char *p;
char username[10]; char username[9];
int i, ret; int i, ret;
struct net_device *dev; struct net_device *dev;
struct iucv_connection **clist = &iucv_conns.iucv_connections;
unsigned long flags;
IUCV_DBF_TEXT(trace, 3, __FUNCTION__); IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
if (count>9) { if (count>9) {
...@@ -1960,7 +1990,7 @@ conn_write(struct device_driver *drv, const char *buf, size_t count) ...@@ -1960,7 +1990,7 @@ conn_write(struct device_driver *drv, const char *buf, size_t count)
for (i=0, p=(char *)buf; i<8 && *p; i++, p++) { for (i=0, p=(char *)buf; i<8 && *p; i++, p++) {
if (isalnum(*p) || (*p == '$')) if (isalnum(*p) || (*p == '$'))
username[i]= *p; username[i]= toupper(*p);
else if (*p == '\n') { else if (*p == '\n') {
/* trailing lf, grr */ /* trailing lf, grr */
break; break;
...@@ -1971,9 +2001,22 @@ conn_write(struct device_driver *drv, const char *buf, size_t count) ...@@ -1971,9 +2001,22 @@ conn_write(struct device_driver *drv, const char *buf, size_t count)
return -EINVAL; return -EINVAL;
} }
} }
while (i<9) while (i<8)
username[i++] = ' '; username[i++] = ' ';
username[9] = '\0'; username[8] = '\0';
read_lock_irqsave(&iucv_conns.iucv_rwlock, flags);
while (*clist) {
if (!strncmp(username, (*clist)->userid, 9))
break;
clist = &((*clist)->next);
}
read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
if (*clist) {
PRINT_WARN("netiucv: Connection to %s already exists\n",
username);
return -EEXIST;
}
dev = netiucv_init_netdevice(username); dev = netiucv_init_netdevice(username);
if (!dev) { if (!dev) {
PRINT_WARN( PRINT_WARN(
...@@ -2015,7 +2058,8 @@ DRIVER_ATTR(connection, 0200, NULL, conn_write); ...@@ -2015,7 +2058,8 @@ DRIVER_ATTR(connection, 0200, NULL, conn_write);
static ssize_t static ssize_t
remove_write (struct device_driver *drv, const char *buf, size_t count) remove_write (struct device_driver *drv, const char *buf, size_t count)
{ {
struct iucv_connection **clist = &iucv_connections; struct iucv_connection **clist = &iucv_conns.iucv_connections;
unsigned long flags;
struct net_device *ndev; struct net_device *ndev;
struct netiucv_priv *priv; struct netiucv_priv *priv;
struct device *dev; struct device *dev;
...@@ -2026,7 +2070,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count) ...@@ -2026,7 +2070,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count)
IUCV_DBF_TEXT(trace, 3, __FUNCTION__); IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
if (count >= IFNAMSIZ) if (count >= IFNAMSIZ)
count = IFNAMSIZ-1; count = IFNAMSIZ - 1;;
for (i=0, p=(char *)buf; i<count && *p; i++, p++) { for (i=0, p=(char *)buf; i<count && *p; i++, p++) {
if ((*p == '\n') || (*p == ' ')) { if ((*p == '\n') || (*p == ' ')) {
...@@ -2038,6 +2082,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count) ...@@ -2038,6 +2082,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count)
} }
name[i] = '\0'; name[i] = '\0';
read_lock_irqsave(&iucv_conns.iucv_rwlock, flags);
while (*clist) { while (*clist) {
ndev = (*clist)->netdev; ndev = (*clist)->netdev;
priv = (struct netiucv_priv*)ndev->priv; priv = (struct netiucv_priv*)ndev->priv;
...@@ -2047,6 +2092,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count) ...@@ -2047,6 +2092,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count)
clist = &((*clist)->next); clist = &((*clist)->next);
continue; continue;
} }
read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
if (ndev->flags & (IFF_UP | IFF_RUNNING)) { if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
PRINT_WARN( PRINT_WARN(
"netiucv: net device %s active with peer %s\n", "netiucv: net device %s active with peer %s\n",
...@@ -2060,6 +2106,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count) ...@@ -2060,6 +2106,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count)
netiucv_unregister_device(dev); netiucv_unregister_device(dev);
return count; return count;
} }
read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags);
PRINT_WARN("netiucv: net device %s unknown\n", name); PRINT_WARN("netiucv: net device %s unknown\n", name);
IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n"); IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n");
return -EINVAL; return -EINVAL;
...@@ -2077,8 +2124,8 @@ static void __exit ...@@ -2077,8 +2124,8 @@ static void __exit
netiucv_exit(void) netiucv_exit(void)
{ {
IUCV_DBF_TEXT(trace, 3, __FUNCTION__); IUCV_DBF_TEXT(trace, 3, __FUNCTION__);
while (iucv_connections) { while (iucv_conns.iucv_connections) {
struct net_device *ndev = iucv_connections->netdev; struct net_device *ndev = iucv_conns.iucv_connections->netdev;
struct netiucv_priv *priv = (struct netiucv_priv*)ndev->priv; struct netiucv_priv *priv = (struct netiucv_priv*)ndev->priv;
struct device *dev = priv->dev; struct device *dev = priv->dev;
...@@ -2120,6 +2167,7 @@ netiucv_init(void) ...@@ -2120,6 +2167,7 @@ netiucv_init(void)
if (!ret) { if (!ret) {
ret = driver_create_file(&netiucv_driver, &driver_attr_remove); ret = driver_create_file(&netiucv_driver, &driver_attr_remove);
netiucv_banner(); netiucv_banner();
rwlock_init(&iucv_conns.iucv_rwlock);
} else { } else {
PRINT_ERR("NETIUCV: failed to add driver attribute.\n"); PRINT_ERR("NETIUCV: failed to add driver attribute.\n");
IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_create_file\n", ret); IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_create_file\n", ret);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment