1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright IBM Corp. 2007, 2007 |
4 | * Authors: Peter Tiedemann (ptiedem@de.ibm.com) |
5 | * |
6 | */ |
7 | |
8 | #undef DEBUG |
9 | #undef DEBUGDATA |
10 | #undef DEBUGCCW |
11 | |
12 | #define KMSG_COMPONENT "ctcm" |
13 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
14 | |
15 | #include <linux/device.h> |
16 | #include <linux/sysfs.h> |
17 | #include <linux/slab.h> |
18 | #include "ctcm_main.h" |
19 | |
20 | /* |
21 | * sysfs attributes |
22 | */ |
23 | |
24 | static ssize_t ctcm_buffer_show(struct device *dev, |
25 | struct device_attribute *attr, char *buf) |
26 | { |
27 | struct ctcm_priv *priv = dev_get_drvdata(dev); |
28 | |
29 | if (!priv) |
30 | return -ENODEV; |
31 | return sysfs_emit(buf, fmt: "%d\n" , priv->buffer_size); |
32 | } |
33 | |
34 | static ssize_t ctcm_buffer_write(struct device *dev, |
35 | struct device_attribute *attr, const char *buf, size_t count) |
36 | { |
37 | struct net_device *ndev; |
38 | unsigned int bs1; |
39 | struct ctcm_priv *priv = dev_get_drvdata(dev); |
40 | int rc; |
41 | |
42 | if (!(priv && priv->channel[CTCM_READ] && |
43 | priv->channel[CTCM_READ]->netdev)) { |
44 | CTCM_DBF_TEXT(SETUP, CTC_DBF_ERROR, "bfnondev" ); |
45 | return -ENODEV; |
46 | } |
47 | ndev = priv->channel[CTCM_READ]->netdev; |
48 | |
49 | rc = kstrtouint(s: buf, base: 0, res: &bs1); |
50 | if (rc) |
51 | goto einval; |
52 | if (bs1 > CTCM_BUFSIZE_LIMIT) |
53 | goto einval; |
54 | if (bs1 < (576 + LL_HEADER_LENGTH + 2)) |
55 | goto einval; |
56 | priv->buffer_size = bs1; /* just to overwrite the default */ |
57 | |
58 | if ((ndev->flags & IFF_RUNNING) && |
59 | (bs1 < (ndev->mtu + LL_HEADER_LENGTH + 2))) |
60 | goto einval; |
61 | |
62 | priv->channel[CTCM_READ]->max_bufsize = bs1; |
63 | priv->channel[CTCM_WRITE]->max_bufsize = bs1; |
64 | if (!(ndev->flags & IFF_RUNNING)) |
65 | ndev->mtu = bs1 - LL_HEADER_LENGTH - 2; |
66 | priv->channel[CTCM_READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED; |
67 | priv->channel[CTCM_WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED; |
68 | |
69 | CTCM_DBF_DEV(SETUP, ndev, buf); |
70 | return count; |
71 | |
72 | einval: |
73 | CTCM_DBF_DEV(SETUP, ndev, "buff_err" ); |
74 | return -EINVAL; |
75 | } |
76 | |
77 | static void ctcm_print_statistics(struct ctcm_priv *priv) |
78 | { |
79 | char *sbuf; |
80 | char *p; |
81 | |
82 | if (!priv) |
83 | return; |
84 | sbuf = kmalloc(size: 2048, GFP_KERNEL); |
85 | if (sbuf == NULL) |
86 | return; |
87 | p = sbuf; |
88 | |
89 | p += scnprintf(buf: p, CTCM_STATSIZE_LIMIT, fmt: " Device FSM state: %s\n" , |
90 | fsm_getstate_str(fi: priv->fsm)); |
91 | p += scnprintf(buf: p, CTCM_STATSIZE_LIMIT, fmt: " RX channel FSM state: %s\n" , |
92 | fsm_getstate_str(fi: priv->channel[CTCM_READ]->fsm)); |
93 | p += scnprintf(buf: p, CTCM_STATSIZE_LIMIT, fmt: " TX channel FSM state: %s\n" , |
94 | fsm_getstate_str(fi: priv->channel[CTCM_WRITE]->fsm)); |
95 | p += scnprintf(buf: p, CTCM_STATSIZE_LIMIT, fmt: " Max. TX buffer used: %ld\n" , |
96 | priv->channel[WRITE]->prof.maxmulti); |
97 | p += scnprintf(buf: p, CTCM_STATSIZE_LIMIT, fmt: " Max. chained SKBs: %ld\n" , |
98 | priv->channel[WRITE]->prof.maxcqueue); |
99 | p += scnprintf(buf: p, CTCM_STATSIZE_LIMIT, fmt: " TX single write ops: %ld\n" , |
100 | priv->channel[WRITE]->prof.doios_single); |
101 | p += scnprintf(buf: p, CTCM_STATSIZE_LIMIT, fmt: " TX multi write ops: %ld\n" , |
102 | priv->channel[WRITE]->prof.doios_multi); |
103 | p += scnprintf(buf: p, CTCM_STATSIZE_LIMIT, fmt: " Netto bytes written: %ld\n" , |
104 | priv->channel[WRITE]->prof.txlen); |
105 | p += scnprintf(buf: p, CTCM_STATSIZE_LIMIT, fmt: " Max. TX IO-time: %u\n" , |
106 | jiffies_to_usecs(j: priv->channel[WRITE]->prof.tx_time)); |
107 | |
108 | printk(KERN_INFO "Statistics for %s:\n%s" , |
109 | priv->channel[CTCM_WRITE]->netdev->name, sbuf); |
110 | kfree(objp: sbuf); |
111 | return; |
112 | } |
113 | |
114 | static ssize_t stats_show(struct device *dev, |
115 | struct device_attribute *attr, char *buf) |
116 | { |
117 | struct ccwgroup_device *gdev = to_ccwgroupdev(dev); |
118 | struct ctcm_priv *priv = dev_get_drvdata(dev); |
119 | |
120 | if (!priv || gdev->state != CCWGROUP_ONLINE) |
121 | return -ENODEV; |
122 | ctcm_print_statistics(priv); |
123 | return sysfs_emit(buf, fmt: "0\n" ); |
124 | } |
125 | |
126 | static ssize_t stats_write(struct device *dev, struct device_attribute *attr, |
127 | const char *buf, size_t count) |
128 | { |
129 | struct ctcm_priv *priv = dev_get_drvdata(dev); |
130 | if (!priv) |
131 | return -ENODEV; |
132 | /* Reset statistics */ |
133 | memset(&priv->channel[WRITE]->prof, 0, |
134 | sizeof(priv->channel[CTCM_WRITE]->prof)); |
135 | return count; |
136 | } |
137 | |
138 | static ssize_t ctcm_proto_show(struct device *dev, |
139 | struct device_attribute *attr, char *buf) |
140 | { |
141 | struct ctcm_priv *priv = dev_get_drvdata(dev); |
142 | if (!priv) |
143 | return -ENODEV; |
144 | |
145 | return sysfs_emit(buf, fmt: "%d\n" , priv->protocol); |
146 | } |
147 | |
148 | static ssize_t ctcm_proto_store(struct device *dev, |
149 | struct device_attribute *attr, const char *buf, size_t count) |
150 | { |
151 | int value, rc; |
152 | struct ctcm_priv *priv = dev_get_drvdata(dev); |
153 | |
154 | if (!priv) |
155 | return -ENODEV; |
156 | rc = kstrtoint(s: buf, base: 0, res: &value); |
157 | if (rc || |
158 | !((value == CTCM_PROTO_S390) || |
159 | (value == CTCM_PROTO_LINUX) || |
160 | (value == CTCM_PROTO_MPC) || |
161 | (value == CTCM_PROTO_OS390))) |
162 | return -EINVAL; |
163 | priv->protocol = value; |
164 | CTCM_DBF_DEV(SETUP, dev, buf); |
165 | |
166 | return count; |
167 | } |
168 | |
169 | static const char *ctcm_type[] = { |
170 | "not a channel" , |
171 | "CTC/A" , |
172 | "FICON channel" , |
173 | "ESCON channel" , |
174 | "unknown channel type" , |
175 | "unsupported channel type" , |
176 | }; |
177 | |
178 | static ssize_t ctcm_type_show(struct device *dev, |
179 | struct device_attribute *attr, char *buf) |
180 | { |
181 | struct ccwgroup_device *cgdev; |
182 | |
183 | cgdev = to_ccwgroupdev(dev); |
184 | if (!cgdev) |
185 | return -ENODEV; |
186 | |
187 | return sysfs_emit(buf, fmt: "%s\n" , |
188 | ctcm_type[cgdev->cdev[0]->id.driver_info]); |
189 | } |
190 | |
191 | static DEVICE_ATTR(buffer, 0644, ctcm_buffer_show, ctcm_buffer_write); |
192 | static DEVICE_ATTR(protocol, 0644, ctcm_proto_show, ctcm_proto_store); |
193 | static DEVICE_ATTR(type, 0444, ctcm_type_show, NULL); |
194 | static DEVICE_ATTR(stats, 0644, stats_show, stats_write); |
195 | |
196 | static struct attribute *ctcm_attr[] = { |
197 | &dev_attr_protocol.attr, |
198 | &dev_attr_type.attr, |
199 | &dev_attr_buffer.attr, |
200 | &dev_attr_stats.attr, |
201 | NULL, |
202 | }; |
203 | |
204 | static struct attribute_group ctcm_attr_group = { |
205 | .attrs = ctcm_attr, |
206 | }; |
207 | const struct attribute_group *ctcm_attr_groups[] = { |
208 | &ctcm_attr_group, |
209 | NULL, |
210 | }; |
211 | |