diff options
author | Frank Pavlic <fpavlic@de.ibm.com> | 2006-09-15 10:25:19 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-09-17 01:03:07 -0400 |
commit | 16a83b30772ad9f20d4233f8872405ad52165cd0 (patch) | |
tree | 1cb9ff33f3dfe46ef5fa956c59fe7bc331443129 /drivers/s390 | |
parent | 4c7ae6ea595aef732d029647d708eaeac7238036 (diff) |
[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: Frank Pavlic <fpavlic@de.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/net/netiucv.c | 80 |
1 files changed, 64 insertions, 16 deletions
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 5d6e6cbfa360..d7d1cc0a5c8e 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c | |||
@@ -112,7 +112,12 @@ struct iucv_connection { | |||
112 | /** | 112 | /** |
113 | * Linked list of all connection structs. | 113 | * Linked list of all connection structs. |
114 | */ | 114 | */ |
115 | static struct iucv_connection *iucv_connections; | 115 | struct iucv_connection_struct { |
116 | struct iucv_connection *iucv_connections; | ||
117 | rwlock_t iucv_rwlock; | ||
118 | }; | ||
119 | |||
120 | static struct iucv_connection_struct iucv_conns; | ||
116 | 121 | ||
117 | /** | 122 | /** |
118 | * Representation of event-data for the | 123 | * Representation of event-data for the |
@@ -1368,8 +1373,10 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf, | |||
1368 | struct net_device *ndev = priv->conn->netdev; | 1373 | struct net_device *ndev = priv->conn->netdev; |
1369 | char *p; | 1374 | char *p; |
1370 | char *tmp; | 1375 | char *tmp; |
1371 | char username[10]; | 1376 | char username[9]; |
1372 | int i; | 1377 | int i; |
1378 | struct iucv_connection **clist = &iucv_conns.iucv_connections; | ||
1379 | unsigned long flags; | ||
1373 | 1380 | ||
1374 | IUCV_DBF_TEXT(trace, 3, __FUNCTION__); | 1381 | IUCV_DBF_TEXT(trace, 3, __FUNCTION__); |
1375 | if (count>9) { | 1382 | if (count>9) { |
@@ -1382,7 +1389,7 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf, | |||
1382 | tmp = strsep((char **) &buf, "\n"); | 1389 | tmp = strsep((char **) &buf, "\n"); |
1383 | for (i=0, p=tmp; i<8 && *p; i++, p++) { | 1390 | for (i=0, p=tmp; i<8 && *p; i++, p++) { |
1384 | if (isalnum(*p) || (*p == '$')) | 1391 | if (isalnum(*p) || (*p == '$')) |
1385 | username[i]= *p; | 1392 | username[i]= toupper(*p); |
1386 | else if (*p == '\n') { | 1393 | else if (*p == '\n') { |
1387 | /* trailing lf, grr */ | 1394 | /* trailing lf, grr */ |
1388 | break; | 1395 | break; |
@@ -1395,11 +1402,11 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf, | |||
1395 | return -EINVAL; | 1402 | return -EINVAL; |
1396 | } | 1403 | } |
1397 | } | 1404 | } |
1398 | while (i<9) | 1405 | while (i<8) |
1399 | username[i++] = ' '; | 1406 | username[i++] = ' '; |
1400 | username[9] = '\0'; | 1407 | username[8] = '\0'; |
1401 | 1408 | ||
1402 | if (memcmp(username, priv->conn->userid, 8)) { | 1409 | if (memcmp(username, priv->conn->userid, 9)) { |
1403 | /* username changed */ | 1410 | /* username changed */ |
1404 | if (ndev->flags & (IFF_UP | IFF_RUNNING)) { | 1411 | if (ndev->flags & (IFF_UP | IFF_RUNNING)) { |
1405 | PRINT_WARN( | 1412 | PRINT_WARN( |
@@ -1410,6 +1417,19 @@ user_write (struct device *dev, struct device_attribute *attr, const char *buf, | |||
1410 | return -EBUSY; | 1417 | return -EBUSY; |
1411 | } | 1418 | } |
1412 | } | 1419 | } |
1420 | read_lock_irqsave(&iucv_conns.iucv_rwlock, flags); | ||
1421 | while (*clist) { | ||
1422 | if (!strncmp(username, (*clist)->userid, 9) || | ||
1423 | ((*clist)->netdev != ndev)) | ||
1424 | break; | ||
1425 | clist = &((*clist)->next); | ||
1426 | } | ||
1427 | read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags); | ||
1428 | if (*clist) { | ||
1429 | PRINT_WARN("netiucv: Connection to %s already exists\n", | ||
1430 | username); | ||
1431 | return -EEXIST; | ||
1432 | } | ||
1413 | memcpy(priv->conn->userid, username, 9); | 1433 | memcpy(priv->conn->userid, username, 9); |
1414 | 1434 | ||
1415 | return count; | 1435 | return count; |
@@ -1781,13 +1801,15 @@ netiucv_unregister_device(struct device *dev) | |||
1781 | static struct iucv_connection * | 1801 | static struct iucv_connection * |
1782 | netiucv_new_connection(struct net_device *dev, char *username) | 1802 | netiucv_new_connection(struct net_device *dev, char *username) |
1783 | { | 1803 | { |
1784 | struct iucv_connection **clist = &iucv_connections; | 1804 | unsigned long flags; |
1805 | struct iucv_connection **clist = &iucv_conns.iucv_connections; | ||
1785 | struct iucv_connection *conn = | 1806 | struct iucv_connection *conn = |
1786 | kzalloc(sizeof(struct iucv_connection), GFP_KERNEL); | 1807 | kzalloc(sizeof(struct iucv_connection), GFP_KERNEL); |
1787 | 1808 | ||
1788 | if (conn) { | 1809 | if (conn) { |
1789 | skb_queue_head_init(&conn->collect_queue); | 1810 | skb_queue_head_init(&conn->collect_queue); |
1790 | skb_queue_head_init(&conn->commit_queue); | 1811 | skb_queue_head_init(&conn->commit_queue); |
1812 | spin_lock_init(&conn->collect_lock); | ||
1791 | conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT; | 1813 | conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT; |
1792 | conn->netdev = dev; | 1814 | conn->netdev = dev; |
1793 | 1815 | ||
@@ -1822,8 +1844,10 @@ netiucv_new_connection(struct net_device *dev, char *username) | |||
1822 | fsm_newstate(conn->fsm, CONN_STATE_STOPPED); | 1844 | fsm_newstate(conn->fsm, CONN_STATE_STOPPED); |
1823 | } | 1845 | } |
1824 | 1846 | ||
1847 | write_lock_irqsave(&iucv_conns.iucv_rwlock, flags); | ||
1825 | conn->next = *clist; | 1848 | conn->next = *clist; |
1826 | *clist = conn; | 1849 | *clist = conn; |
1850 | write_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags); | ||
1827 | } | 1851 | } |
1828 | return conn; | 1852 | return conn; |
1829 | } | 1853 | } |
@@ -1835,14 +1859,17 @@ netiucv_new_connection(struct net_device *dev, char *username) | |||
1835 | static void | 1859 | static void |
1836 | netiucv_remove_connection(struct iucv_connection *conn) | 1860 | netiucv_remove_connection(struct iucv_connection *conn) |
1837 | { | 1861 | { |
1838 | struct iucv_connection **clist = &iucv_connections; | 1862 | struct iucv_connection **clist = &iucv_conns.iucv_connections; |
1863 | unsigned long flags; | ||
1839 | 1864 | ||
1840 | IUCV_DBF_TEXT(trace, 3, __FUNCTION__); | 1865 | IUCV_DBF_TEXT(trace, 3, __FUNCTION__); |
1841 | if (conn == NULL) | 1866 | if (conn == NULL) |
1842 | return; | 1867 | return; |
1868 | write_lock_irqsave(&iucv_conns.iucv_rwlock, flags); | ||
1843 | while (*clist) { | 1869 | while (*clist) { |
1844 | if (*clist == conn) { | 1870 | if (*clist == conn) { |
1845 | *clist = conn->next; | 1871 | *clist = conn->next; |
1872 | write_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags); | ||
1846 | if (conn->handle) { | 1873 | if (conn->handle) { |
1847 | iucv_unregister_program(conn->handle); | 1874 | iucv_unregister_program(conn->handle); |
1848 | conn->handle = NULL; | 1875 | conn->handle = NULL; |
@@ -1855,6 +1882,7 @@ netiucv_remove_connection(struct iucv_connection *conn) | |||
1855 | } | 1882 | } |
1856 | clist = &((*clist)->next); | 1883 | clist = &((*clist)->next); |
1857 | } | 1884 | } |
1885 | write_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags); | ||
1858 | } | 1886 | } |
1859 | 1887 | ||
1860 | /** | 1888 | /** |
@@ -1947,9 +1975,11 @@ static ssize_t | |||
1947 | conn_write(struct device_driver *drv, const char *buf, size_t count) | 1975 | conn_write(struct device_driver *drv, const char *buf, size_t count) |
1948 | { | 1976 | { |
1949 | char *p; | 1977 | char *p; |
1950 | char username[10]; | 1978 | char username[9]; |
1951 | int i, ret; | 1979 | int i, ret; |
1952 | struct net_device *dev; | 1980 | struct net_device *dev; |
1981 | struct iucv_connection **clist = &iucv_conns.iucv_connections; | ||
1982 | unsigned long flags; | ||
1953 | 1983 | ||
1954 | IUCV_DBF_TEXT(trace, 3, __FUNCTION__); | 1984 | IUCV_DBF_TEXT(trace, 3, __FUNCTION__); |
1955 | if (count>9) { | 1985 | if (count>9) { |
@@ -1960,7 +1990,7 @@ conn_write(struct device_driver *drv, const char *buf, size_t count) | |||
1960 | 1990 | ||
1961 | for (i=0, p=(char *)buf; i<8 && *p; i++, p++) { | 1991 | for (i=0, p=(char *)buf; i<8 && *p; i++, p++) { |
1962 | if (isalnum(*p) || (*p == '$')) | 1992 | if (isalnum(*p) || (*p == '$')) |
1963 | username[i]= *p; | 1993 | username[i]= toupper(*p); |
1964 | else if (*p == '\n') { | 1994 | else if (*p == '\n') { |
1965 | /* trailing lf, grr */ | 1995 | /* trailing lf, grr */ |
1966 | break; | 1996 | break; |
@@ -1971,9 +2001,22 @@ conn_write(struct device_driver *drv, const char *buf, size_t count) | |||
1971 | return -EINVAL; | 2001 | return -EINVAL; |
1972 | } | 2002 | } |
1973 | } | 2003 | } |
1974 | while (i<9) | 2004 | while (i<8) |
1975 | username[i++] = ' '; | 2005 | username[i++] = ' '; |
1976 | username[9] = '\0'; | 2006 | username[8] = '\0'; |
2007 | |||
2008 | read_lock_irqsave(&iucv_conns.iucv_rwlock, flags); | ||
2009 | while (*clist) { | ||
2010 | if (!strncmp(username, (*clist)->userid, 9)) | ||
2011 | break; | ||
2012 | clist = &((*clist)->next); | ||
2013 | } | ||
2014 | read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags); | ||
2015 | if (*clist) { | ||
2016 | PRINT_WARN("netiucv: Connection to %s already exists\n", | ||
2017 | username); | ||
2018 | return -EEXIST; | ||
2019 | } | ||
1977 | dev = netiucv_init_netdevice(username); | 2020 | dev = netiucv_init_netdevice(username); |
1978 | if (!dev) { | 2021 | if (!dev) { |
1979 | PRINT_WARN( | 2022 | PRINT_WARN( |
@@ -2015,7 +2058,8 @@ DRIVER_ATTR(connection, 0200, NULL, conn_write); | |||
2015 | static ssize_t | 2058 | static ssize_t |
2016 | remove_write (struct device_driver *drv, const char *buf, size_t count) | 2059 | remove_write (struct device_driver *drv, const char *buf, size_t count) |
2017 | { | 2060 | { |
2018 | struct iucv_connection **clist = &iucv_connections; | 2061 | struct iucv_connection **clist = &iucv_conns.iucv_connections; |
2062 | unsigned long flags; | ||
2019 | struct net_device *ndev; | 2063 | struct net_device *ndev; |
2020 | struct netiucv_priv *priv; | 2064 | struct netiucv_priv *priv; |
2021 | struct device *dev; | 2065 | struct device *dev; |
@@ -2026,7 +2070,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count) | |||
2026 | IUCV_DBF_TEXT(trace, 3, __FUNCTION__); | 2070 | IUCV_DBF_TEXT(trace, 3, __FUNCTION__); |
2027 | 2071 | ||
2028 | if (count >= IFNAMSIZ) | 2072 | if (count >= IFNAMSIZ) |
2029 | count = IFNAMSIZ-1; | 2073 | count = IFNAMSIZ - 1;; |
2030 | 2074 | ||
2031 | for (i=0, p=(char *)buf; i<count && *p; i++, p++) { | 2075 | for (i=0, p=(char *)buf; i<count && *p; i++, p++) { |
2032 | if ((*p == '\n') || (*p == ' ')) { | 2076 | if ((*p == '\n') || (*p == ' ')) { |
@@ -2038,6 +2082,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count) | |||
2038 | } | 2082 | } |
2039 | name[i] = '\0'; | 2083 | name[i] = '\0'; |
2040 | 2084 | ||
2085 | read_lock_irqsave(&iucv_conns.iucv_rwlock, flags); | ||
2041 | while (*clist) { | 2086 | while (*clist) { |
2042 | ndev = (*clist)->netdev; | 2087 | ndev = (*clist)->netdev; |
2043 | priv = (struct netiucv_priv*)ndev->priv; | 2088 | priv = (struct netiucv_priv*)ndev->priv; |
@@ -2047,6 +2092,7 @@ remove_write (struct device_driver *drv, const char *buf, size_t count) | |||
2047 | clist = &((*clist)->next); | 2092 | clist = &((*clist)->next); |
2048 | continue; | 2093 | continue; |
2049 | } | 2094 | } |
2095 | read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags); | ||
2050 | if (ndev->flags & (IFF_UP | IFF_RUNNING)) { | 2096 | if (ndev->flags & (IFF_UP | IFF_RUNNING)) { |
2051 | PRINT_WARN( | 2097 | PRINT_WARN( |
2052 | "netiucv: net device %s active with peer %s\n", | 2098 | "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 | netiucv_unregister_device(dev); | 2106 | netiucv_unregister_device(dev); |
2061 | return count; | 2107 | return count; |
2062 | } | 2108 | } |
2109 | read_unlock_irqrestore(&iucv_conns.iucv_rwlock, flags); | ||
2063 | PRINT_WARN("netiucv: net device %s unknown\n", name); | 2110 | PRINT_WARN("netiucv: net device %s unknown\n", name); |
2064 | IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n"); | 2111 | IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n"); |
2065 | return -EINVAL; | 2112 | return -EINVAL; |
@@ -2077,8 +2124,8 @@ static void __exit | |||
2077 | netiucv_exit(void) | 2124 | netiucv_exit(void) |
2078 | { | 2125 | { |
2079 | IUCV_DBF_TEXT(trace, 3, __FUNCTION__); | 2126 | IUCV_DBF_TEXT(trace, 3, __FUNCTION__); |
2080 | while (iucv_connections) { | 2127 | while (iucv_conns.iucv_connections) { |
2081 | struct net_device *ndev = iucv_connections->netdev; | 2128 | struct net_device *ndev = iucv_conns.iucv_connections->netdev; |
2082 | struct netiucv_priv *priv = (struct netiucv_priv*)ndev->priv; | 2129 | struct netiucv_priv *priv = (struct netiucv_priv*)ndev->priv; |
2083 | struct device *dev = priv->dev; | 2130 | struct device *dev = priv->dev; |
2084 | 2131 | ||
@@ -2120,6 +2167,7 @@ netiucv_init(void) | |||
2120 | if (!ret) { | 2167 | if (!ret) { |
2121 | ret = driver_create_file(&netiucv_driver, &driver_attr_remove); | 2168 | ret = driver_create_file(&netiucv_driver, &driver_attr_remove); |
2122 | netiucv_banner(); | 2169 | netiucv_banner(); |
2170 | rwlock_init(&iucv_conns.iucv_rwlock); | ||
2123 | } else { | 2171 | } else { |
2124 | PRINT_ERR("NETIUCV: failed to add driver attribute.\n"); | 2172 | PRINT_ERR("NETIUCV: failed to add driver attribute.\n"); |
2125 | IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_create_file\n", ret); | 2173 | IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_create_file\n", ret); |