Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 *
4 * Copyright (C) International Business Machines Corp., 2000,2005
5 *
6 * Modified by Steve French (sfrench@us.ibm.com)
7 */
8#include <linux/fs.h>
9#include <linux/string.h>
10#include <linux/ctype.h>
11#include <linux/kstrtox.h>
12#include <linux/module.h>
13#include <linux/proc_fs.h>
14#include <linux/uaccess.h>
15#include <uapi/linux/ethtool.h>
16#include "cifsglob.h"
17#include "cifsproto.h"
18#include "cifs_debug.h"
19#include "cifsfs.h"
20#include "fs_context.h"
21#ifdef CONFIG_CIFS_DFS_UPCALL
22#include "dfs_cache.h"
23#endif
24#ifdef CONFIG_CIFS_SMB_DIRECT
25#include "smbdirect.h"
26#endif
27#include "cifs_swn.h"
28#include "cached_dir.h"
29
30void
31cifs_dump_mem(char *label, void *data, int length)
32{
33 pr_debug("%s: dump of %d bytes of data at 0x%p\n", label, length, data);
34 print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 4,
35 data, length, true);
36}
37
38void cifs_dump_mids(struct TCP_Server_Info *server)
39{
40#ifdef CONFIG_CIFS_DEBUG2
41 struct mid_q_entry *mid_entry;
42
43 if (server == NULL)
44 return;
45
46 cifs_dbg(VFS, "Dump pending requests:\n");
47 spin_lock(&server->mid_queue_lock);
48 list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
49 cifs_dbg(VFS, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %llu\n",
50 mid_entry->mid_state,
51 le16_to_cpu(mid_entry->command),
52 mid_entry->pid,
53 mid_entry->callback_data,
54 mid_entry->mid);
55#ifdef CONFIG_CIFS_STATS2
56 cifs_dbg(VFS, "IsLarge: %d buf: %p time rcv: %ld now: %ld\n",
57 mid_entry->large_buf,
58 mid_entry->resp_buf,
59 mid_entry->when_received,
60 jiffies);
61#endif /* STATS2 */
62 cifs_dbg(VFS, "IsMult: %d IsEnd: %d\n",
63 mid_entry->multiRsp, mid_entry->multiEnd);
64 if (mid_entry->resp_buf) {
65 server->ops->dump_detail(mid_entry->resp_buf,
66 mid_entry->response_pdu_len, server);
67 cifs_dump_mem("existing buf: ", mid_entry->resp_buf, 62);
68 }
69 }
70 spin_unlock(&server->mid_queue_lock);
71#endif /* CONFIG_CIFS_DEBUG2 */
72}
73
74#ifdef CONFIG_PROC_FS
75static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon)
76{
77 __u32 dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
78
79 seq_printf(m, "%s Mounts: %d ", tcon->tree_name, tcon->tc_count);
80 if (tcon->nativeFileSystem)
81 seq_printf(m, "Type: %s ", tcon->nativeFileSystem);
82 seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x\n\tPathComponentMax: %d Status: %d",
83 le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
84 le32_to_cpu(tcon->fsAttrInfo.Attributes),
85 le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
86 tcon->status);
87 if (dev_type == FILE_DEVICE_DISK)
88 seq_puts(m, " type: DISK ");
89 else if (dev_type == FILE_DEVICE_CD_ROM)
90 seq_puts(m, " type: CDROM ");
91 else
92 seq_printf(m, " type: %d ", dev_type);
93
94 seq_printf(m, "Serial Number: 0x%x", tcon->vol_serial_number);
95
96 if ((tcon->seal) ||
97 (tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) ||
98 (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA))
99 seq_puts(m, " encrypted");
100 if (tcon->nocase)
101 seq_printf(m, " nocase");
102 if (tcon->unix_ext)
103 seq_printf(m, " POSIX Extensions");
104 if (tcon->ses->server->ops->dump_share_caps)
105 tcon->ses->server->ops->dump_share_caps(m, tcon);
106 if (tcon->use_witness)
107 seq_puts(m, " Witness");
108 if (tcon->broken_sparse_sup)
109 seq_puts(m, " nosparse");
110 if (tcon->need_reconnect)
111 seq_puts(m, "\tDISCONNECTED ");
112 spin_lock(&tcon->tc_lock);
113 if (tcon->origin_fullpath) {
114 seq_printf(m, "\n\tDFS origin fullpath: %s",
115 tcon->origin_fullpath);
116 }
117 spin_unlock(&tcon->tc_lock);
118 seq_putc(m, '\n');
119}
120
121static void
122cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
123{
124 struct TCP_Server_Info *server = chan->server;
125
126 if (!server) {
127 seq_printf(m, "\n\n\t\tChannel: %d DISABLED", i+1);
128 return;
129 }
130
131 seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx"
132 "\n\t\tNumber of credits: %d,%d,%d Dialect 0x%x"
133 "\n\t\tTCP status: %d Instance: %d"
134 "\n\t\tLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d"
135 "\n\t\tIn Send: %d In MaxReq Wait: %d",
136 i+1, server->conn_id,
137 server->credits,
138 server->echo_credits,
139 server->oplock_credits,
140 server->dialect,
141 server->tcpStatus,
142 server->reconnect_instance,
143 server->srv_count,
144 server->sec_mode,
145 in_flight(server),
146 atomic_read(&server->in_send),
147 atomic_read(&server->num_waiters));
148#ifdef CONFIG_NET_NS
149 if (server->net)
150 seq_printf(m, " Net namespace: %u ", server->net->ns.inum);
151#endif /* NET_NS */
152
153}
154
155static inline const char *smb_speed_to_str(size_t bps)
156{
157 size_t mbps = bps / 1000 / 1000;
158
159 switch (mbps) {
160 case SPEED_10:
161 return "10Mbps";
162 case SPEED_100:
163 return "100Mbps";
164 case SPEED_1000:
165 return "1Gbps";
166 case SPEED_2500:
167 return "2.5Gbps";
168 case SPEED_5000:
169 return "5Gbps";
170 case SPEED_10000:
171 return "10Gbps";
172 case SPEED_14000:
173 return "14Gbps";
174 case SPEED_20000:
175 return "20Gbps";
176 case SPEED_25000:
177 return "25Gbps";
178 case SPEED_40000:
179 return "40Gbps";
180 case SPEED_50000:
181 return "50Gbps";
182 case SPEED_56000:
183 return "56Gbps";
184 case SPEED_100000:
185 return "100Gbps";
186 case SPEED_200000:
187 return "200Gbps";
188 case SPEED_400000:
189 return "400Gbps";
190 case SPEED_800000:
191 return "800Gbps";
192 default:
193 return "Unknown";
194 }
195}
196
197static void
198cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface)
199{
200 struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
201 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
202
203 seq_printf(m, "\tSpeed: %s\n", smb_speed_to_str(iface->speed));
204 seq_puts(m, "\t\tCapabilities: ");
205 if (iface->rdma_capable)
206 seq_puts(m, "rdma ");
207 if (iface->rss_capable)
208 seq_puts(m, "rss ");
209 if (!iface->rdma_capable && !iface->rss_capable)
210 seq_puts(m, "None");
211 seq_putc(m, '\n');
212 if (iface->sockaddr.ss_family == AF_INET)
213 seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr);
214 else if (iface->sockaddr.ss_family == AF_INET6)
215 seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr);
216 if (!iface->is_active)
217 seq_puts(m, "\t\t[for-cleanup]\n");
218}
219
220static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
221{
222 struct TCP_Server_Info *server;
223 struct cifs_ses *ses;
224 struct cifs_tcon *tcon;
225 struct cifsFileInfo *cfile;
226 struct inode *inode;
227 struct cifsInodeInfo *cinode;
228 char lease[4];
229 int n;
230
231 seq_puts(m, "# Version:1\n");
232 seq_puts(m, "# Format:\n");
233 seq_puts(m, "# <tree id> <ses id> <persistent fid> <flags> <count> <pid> <uid>");
234#ifdef CONFIG_CIFS_DEBUG2
235 seq_puts(m, " <filename> <lease> <lease-key> <mid>\n");
236#else
237 seq_puts(m, " <filename> <lease> <lease-key>\n");
238#endif /* CIFS_DEBUG2 */
239 spin_lock(&cifs_tcp_ses_lock);
240 list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
241 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
242 if (cifs_ses_exiting(ses))
243 continue;
244 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
245 spin_lock(&tcon->open_file_lock);
246 list_for_each_entry(cfile, &tcon->openFileList, tlist) {
247 seq_printf(m,
248 "0x%x 0x%llx 0x%llx 0x%x %d %d %d %pd",
249 tcon->tid,
250 ses->Suid,
251 cfile->fid.persistent_fid,
252 cfile->f_flags,
253 cfile->count,
254 cfile->pid,
255 from_kuid(&init_user_ns, cfile->uid),
256 cfile->dentry);
257
258 /* Append lease/oplock caching state as RHW letters */
259 inode = d_inode(cfile->dentry);
260 cinode = NULL;
261 n = 0;
262 if (inode) {
263 cinode = CIFS_I(inode);
264 if (CIFS_CACHE_READ(cinode))
265 lease[n++] = 'R';
266 if (CIFS_CACHE_HANDLE(cinode))
267 lease[n++] = 'H';
268 if (CIFS_CACHE_WRITE(cinode))
269 lease[n++] = 'W';
270 }
271 lease[n] = '\0';
272 seq_puts(m, " ");
273 if (n)
274 seq_printf(m, "%s", lease);
275 else
276 seq_puts(m, "NONE");
277
278 seq_puts(m, " ");
279 if (cinode && cinode->lease_granted)
280 seq_printf(m, "%pUl", cinode->lease_key);
281 else
282 seq_puts(m, "-");
283
284#ifdef CONFIG_CIFS_DEBUG2
285 seq_printf(m, " %llu", cfile->fid.mid);
286#endif /* CONFIG_CIFS_DEBUG2 */
287 seq_printf(m, "\n");
288 }
289 spin_unlock(&tcon->open_file_lock);
290 }
291 }
292 }
293 spin_unlock(&cifs_tcp_ses_lock);
294 seq_putc(m, '\n');
295 return 0;
296}
297
298static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
299{
300 struct list_head *stmp, *tmp, *tmp1;
301 struct TCP_Server_Info *server;
302 struct cifs_ses *ses;
303 struct cifs_tcon *tcon;
304 struct cached_fids *cfids;
305 struct cached_fid *cfid;
306 LIST_HEAD(entry);
307
308 seq_puts(m, "# Version:1\n");
309 seq_puts(m, "# Format:\n");
310 seq_puts(m, "# <tree id> <sess id> <persistent fid> <lease-key> <path>\n");
311
312 spin_lock(&cifs_tcp_ses_lock);
313 list_for_each(stmp, &cifs_tcp_ses_list) {
314 server = list_entry(stmp, struct TCP_Server_Info,
315 tcp_ses_list);
316 list_for_each(tmp, &server->smb_ses_list) {
317 ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
318 list_for_each(tmp1, &ses->tcon_list) {
319 tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
320 cfids = tcon->cfids;
321 if (!cfids)
322 continue;
323 spin_lock(&cfids->cfid_list_lock); /* check lock ordering */
324 seq_printf(m, "Num entries: %d, cached_dirents: %lu entries, %llu bytes\n",
325 cfids->num_entries,
326 (unsigned long)atomic_long_read(&cfids->total_dirents_entries),
327 (unsigned long long)atomic64_read(&cfids->total_dirents_bytes));
328 list_for_each_entry(cfid, &cfids->entries, entry) {
329 seq_printf(m, "0x%x 0x%llx 0x%llx ",
330 tcon->tid,
331 ses->Suid,
332 cfid->fid.persistent_fid);
333 if (cfid->has_lease)
334 seq_printf(m, "%pUl ", cfid->fid.lease_key);
335 else
336 seq_puts(m, "- ");
337 seq_printf(m, "%s", cfid->path);
338 if (cfid->file_all_info_is_valid)
339 seq_printf(m, "\tvalid file info");
340 if (cfid->dirents.is_valid)
341 seq_printf(m, ", valid dirents");
342 if (!list_empty(&cfid->dirents.entries))
343 seq_printf(m, ", dirents: %lu entries, %lu bytes",
344 cfid->dirents.entries_count, cfid->dirents.bytes_used);
345 seq_printf(m, "\n");
346 }
347 spin_unlock(&cfids->cfid_list_lock);
348 }
349 }
350 }
351 spin_unlock(&cifs_tcp_ses_lock);
352 seq_putc(m, '\n');
353 return 0;
354}
355
356static __always_inline const char *compression_alg_str(__le16 alg)
357{
358 switch (alg) {
359 case SMB3_COMPRESS_NONE:
360 return "NONE";
361 case SMB3_COMPRESS_LZNT1:
362 return "LZNT1";
363 case SMB3_COMPRESS_LZ77:
364 return "LZ77";
365 case SMB3_COMPRESS_LZ77_HUFF:
366 return "LZ77-Huffman";
367 case SMB3_COMPRESS_PATTERN:
368 return "Pattern_V1";
369 default:
370 return "invalid";
371 }
372}
373
374static __always_inline const char *cipher_alg_str(__le16 cipher)
375{
376 switch (cipher) {
377 case SMB2_ENCRYPTION_AES128_CCM:
378 return "AES128-CCM";
379 case SMB2_ENCRYPTION_AES128_GCM:
380 return "AES128-GCM";
381 case SMB2_ENCRYPTION_AES256_CCM:
382 return "AES256-CCM";
383 case SMB2_ENCRYPTION_AES256_GCM:
384 return "AES256-GCM";
385 default:
386 return "UNKNOWN";
387 }
388}
389
390static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
391{
392 struct mid_q_entry *mid_entry;
393 struct TCP_Server_Info *server;
394 struct TCP_Server_Info *chan_server;
395 struct cifs_ses *ses;
396 struct cifs_tcon *tcon;
397 struct cifs_server_iface *iface;
398 size_t iface_weight = 0, iface_min_speed = 0;
399 struct cifs_server_iface *last_iface = NULL;
400 int c, i, j;
401
402 seq_puts(m,
403 "Display Internal CIFS Data Structures for Debugging\n"
404 "---------------------------------------------------\n");
405 seq_printf(m, "CIFS Version %s\n", CIFS_VERSION);
406 seq_printf(m, "Features:");
407#ifdef CONFIG_CIFS_DFS_UPCALL
408 seq_printf(m, " DFS");
409#endif
410#ifdef CONFIG_CIFS_FSCACHE
411 seq_printf(m, ",FSCACHE");
412#endif
413#ifdef CONFIG_CIFS_SMB_DIRECT
414 seq_printf(m, ",SMB_DIRECT");
415#endif
416#ifdef CONFIG_CIFS_STATS2
417 seq_printf(m, ",STATS2");
418#else
419 seq_printf(m, ",STATS");
420#endif
421#ifdef CONFIG_CIFS_DEBUG2
422 seq_printf(m, ",DEBUG2");
423#elif defined(CONFIG_CIFS_DEBUG)
424 seq_printf(m, ",DEBUG");
425#endif
426#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
427 seq_printf(m, ",ALLOW_INSECURE_LEGACY");
428#endif
429#ifdef CONFIG_CIFS_POSIX
430 seq_printf(m, ",CIFS_POSIX");
431#endif
432#ifdef CONFIG_CIFS_UPCALL
433 seq_printf(m, ",UPCALL(SPNEGO)");
434#endif
435#ifdef CONFIG_CIFS_XATTR
436 seq_printf(m, ",XATTR");
437#endif
438 seq_printf(m, ",ACL");
439#ifdef CONFIG_CIFS_SWN_UPCALL
440 seq_puts(m, ",WITNESS");
441#endif
442#ifdef CONFIG_CIFS_COMPRESSION
443 seq_puts(m, ",COMPRESSION");
444#endif
445 seq_putc(m, '\n');
446 seq_printf(m, "CIFSMaxBufSize: %d\n", CIFSMaxBufSize);
447 seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid);
448
449 seq_printf(m, "\nServers: ");
450
451 c = 0;
452 spin_lock(&cifs_tcp_ses_lock);
453 list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
454 /* channel info will be printed as a part of sessions below */
455 if (SERVER_IS_CHAN(server))
456 continue;
457
458 c++;
459 seq_printf(m, "\n%d) ConnectionId: 0x%llx ",
460 c, server->conn_id);
461
462 spin_lock(&server->srv_lock);
463 if (server->hostname)
464 seq_printf(m, "Hostname: %s ", server->hostname);
465 seq_printf(m, "\nClientGUID: %pUL", server->client_guid);
466 spin_unlock(&server->srv_lock);
467#ifdef CONFIG_CIFS_SMB_DIRECT
468 smbd_debug_proc_show(server, m);
469#endif
470 seq_printf(m, "\nNumber of credits: %d,%d,%d Dialect 0x%x",
471 server->credits,
472 server->echo_credits,
473 server->oplock_credits,
474 server->dialect);
475 if (server->sign)
476 seq_printf(m, " signed");
477 if (server->posix_ext_supported)
478 seq_printf(m, " posix");
479 if (server->nosharesock)
480 seq_printf(m, " nosharesock");
481
482 seq_printf(m, "\nServer capabilities: 0x%x", server->capabilities);
483
484 if (server->rdma)
485 seq_printf(m, "\nRDMA ");
486 seq_printf(m, "\nTCP status: %d Instance: %d"
487 "\nLocal Users To Server: %d SecMode: 0x%x Req On Wire: %d",
488 server->tcpStatus,
489 server->reconnect_instance,
490 server->srv_count,
491 server->sec_mode, in_flight(server));
492#ifdef CONFIG_NET_NS
493 if (server->net)
494 seq_printf(m, " Net namespace: %u ", server->net->ns.inum);
495#endif /* NET_NS */
496
497 seq_printf(m, "\nIn Send: %d In MaxReq Wait: %d",
498 atomic_read(&server->in_send),
499 atomic_read(&server->num_waiters));
500
501 if (server->leaf_fullpath) {
502 seq_printf(m, "\nDFS leaf full path: %s",
503 server->leaf_fullpath);
504 }
505
506 seq_puts(m, "\nCompression: ");
507 if (!IS_ENABLED(CONFIG_CIFS_COMPRESSION))
508 seq_puts(m, "no built-in support");
509 else if (!server->compression.requested)
510 seq_puts(m, "disabled on mount");
511 else if (server->compression.enabled)
512 seq_printf(m, "enabled (%s)", compression_alg_str(server->compression.alg));
513 else
514 seq_puts(m, "disabled (not supported by this server)");
515
516 /* Show negotiated encryption cipher, even if not required */
517 seq_puts(m, "\nEncryption: ");
518 if (server->cipher_type)
519 seq_printf(m, "Negotiated cipher (%s)", cipher_alg_str(server->cipher_type));
520
521 seq_printf(m, "\n\n\tSessions: ");
522 i = 0;
523 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
524 spin_lock(&ses->ses_lock);
525 if (ses->ses_status == SES_EXITING) {
526 spin_unlock(&ses->ses_lock);
527 continue;
528 }
529 i++;
530 if ((ses->serverDomain == NULL) ||
531 (ses->serverOS == NULL) ||
532 (ses->serverNOS == NULL)) {
533 seq_printf(m, "\n\t%d) Address: %s Uses: %d Capability: 0x%x\tSession Status: %d ",
534 i, ses->ip_addr, ses->ses_count,
535 ses->capabilities, ses->ses_status);
536 if (ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST)
537 seq_printf(m, "Guest ");
538 else if (ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
539 seq_printf(m, "Anonymous ");
540 } else {
541 seq_printf(m,
542 "\n\t%d) Name: %s Domain: %s Uses: %d OS: %s "
543 "\n\tNOS: %s\tCapability: 0x%x"
544 "\n\tSMB session status: %d ",
545 i, ses->ip_addr, ses->serverDomain,
546 ses->ses_count, ses->serverOS, ses->serverNOS,
547 ses->capabilities, ses->ses_status);
548 }
549 if (ses->expired_pwd)
550 seq_puts(m, "password no longer valid ");
551 spin_unlock(&ses->ses_lock);
552
553 seq_printf(m, "\n\tSecurity type: %s ",
554 get_security_type_str(server->ops->select_sectype(server, ses->sectype)));
555
556 /* dump session id helpful for use with network trace */
557 seq_printf(m, " SessionId: 0x%llx", ses->Suid);
558 if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
559 seq_puts(m, " encrypted");
560 if (ses->sign)
561 seq_puts(m, " signed");
562
563 seq_printf(m, "\n\tUser: %d Cred User: %d",
564 from_kuid(&init_user_ns, ses->linux_uid),
565 from_kuid(&init_user_ns, ses->cred_uid));
566
567 if (ses->dfs_root_ses) {
568 seq_printf(m, "\n\tDFS root session id: 0x%llx",
569 ses->dfs_root_ses->Suid);
570 }
571
572 spin_lock(&ses->chan_lock);
573 if (CIFS_CHAN_NEEDS_RECONNECT(ses, 0))
574 seq_puts(m, "\tPrimary channel: DISCONNECTED ");
575 if (CIFS_CHAN_IN_RECONNECT(ses, 0))
576 seq_puts(m, "\t[RECONNECTING] ");
577
578 if (ses->chan_count > 1) {
579 seq_printf(m, "\n\n\tExtra Channels: %zu ",
580 ses->chan_count-1);
581 for (j = 1; j < ses->chan_count; j++) {
582 cifs_dump_channel(m, j, &ses->chans[j]);
583 if (CIFS_CHAN_NEEDS_RECONNECT(ses, j))
584 seq_puts(m, "\tDISCONNECTED ");
585 if (CIFS_CHAN_IN_RECONNECT(ses, j))
586 seq_puts(m, "\t[RECONNECTING] ");
587 }
588 }
589 spin_unlock(&ses->chan_lock);
590
591 seq_puts(m, "\n\n\tShares: ");
592 j = 0;
593
594 seq_printf(m, "\n\t%d) IPC: ", j);
595 if (ses->tcon_ipc)
596 cifs_debug_tcon(m, ses->tcon_ipc);
597 else
598 seq_puts(m, "none\n");
599
600 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
601 ++j;
602 seq_printf(m, "\n\t%d) ", j);
603 cifs_debug_tcon(m, tcon);
604 }
605
606 spin_lock(&ses->iface_lock);
607 if (ses->iface_count)
608 seq_printf(m, "\n\n\tServer interfaces: %zu"
609 "\tLast updated: %lu seconds ago",
610 ses->iface_count,
611 (jiffies - ses->iface_last_update) / HZ);
612
613 last_iface = list_last_entry(&ses->iface_list,
614 struct cifs_server_iface,
615 iface_head);
616 iface_min_speed = last_iface->speed;
617
618 j = 0;
619 list_for_each_entry(iface, &ses->iface_list,
620 iface_head) {
621 seq_printf(m, "\n\t%d)", ++j);
622 cifs_dump_iface(m, iface);
623
624 iface_weight = iface->speed / iface_min_speed;
625 seq_printf(m, "\t\tWeight (cur,total): (%zu,%zu)"
626 "\n\t\tAllocated channels: %u\n",
627 iface->weight_fulfilled,
628 iface_weight,
629 iface->num_channels);
630
631 if (is_ses_using_iface(ses, iface))
632 seq_puts(m, "\t\t[CONNECTED]\n");
633 }
634 spin_unlock(&ses->iface_lock);
635
636 seq_puts(m, "\n\n\tMIDs: ");
637 spin_lock(&ses->chan_lock);
638 for (j = 0; j < ses->chan_count; j++) {
639 chan_server = ses->chans[j].server;
640 if (!chan_server)
641 continue;
642
643 if (list_empty(&chan_server->pending_mid_q))
644 continue;
645
646 seq_printf(m, "\n\tServer ConnectionId: 0x%llx",
647 chan_server->conn_id);
648 spin_lock(&chan_server->mid_queue_lock);
649 list_for_each_entry(mid_entry, &chan_server->pending_mid_q, qhead) {
650 seq_printf(m, "\n\t\tState: %d com: %d pid: %d cbdata: %p mid %llu",
651 mid_entry->mid_state,
652 le16_to_cpu(mid_entry->command),
653 mid_entry->pid,
654 mid_entry->callback_data,
655 mid_entry->mid);
656 }
657 spin_unlock(&chan_server->mid_queue_lock);
658 }
659 spin_unlock(&ses->chan_lock);
660 seq_puts(m, "\n--\n");
661 }
662 if (i == 0)
663 seq_printf(m, "\n\t\t[NONE]");
664 }
665 if (c == 0)
666 seq_printf(m, "\n\t[NONE]");
667
668 spin_unlock(&cifs_tcp_ses_lock);
669 seq_putc(m, '\n');
670 cifs_swn_dump(m);
671
672 /* BB add code to dump additional info such as TCP session info now */
673 return 0;
674}
675
676static ssize_t cifs_stats_proc_write(struct file *file,
677 const char __user *buffer, size_t count, loff_t *ppos)
678{
679 bool bv;
680 int rc;
681 struct TCP_Server_Info *server;
682 struct cifs_ses *ses;
683 struct cifs_tcon *tcon;
684
685 rc = kstrtobool_from_user(buffer, count, &bv);
686 if (rc == 0) {
687#ifdef CONFIG_CIFS_STATS2
688 int i;
689
690 atomic_set(&total_buf_alloc_count, 0);
691 atomic_set(&total_small_buf_alloc_count, 0);
692#endif /* CONFIG_CIFS_STATS2 */
693 atomic_set(&tcpSesReconnectCount, 0);
694 atomic_set(&tconInfoReconnectCount, 0);
695
696 spin_lock(&GlobalMid_Lock);
697 GlobalMaxActiveXid = 0;
698 GlobalCurrentXid = 0;
699 spin_unlock(&GlobalMid_Lock);
700 spin_lock(&cifs_tcp_ses_lock);
701 list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
702 server->max_in_flight = 0;
703#ifdef CONFIG_CIFS_STATS2
704 for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) {
705 atomic_set(&server->num_cmds[i], 0);
706 atomic_set(&server->smb2slowcmd[i], 0);
707 server->time_per_cmd[i] = 0;
708 server->slowest_cmd[i] = 0;
709 server->fastest_cmd[0] = 0;
710 }
711#endif /* CONFIG_CIFS_STATS2 */
712 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
713 if (cifs_ses_exiting(ses))
714 continue;
715 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
716 atomic_set(&tcon->num_smbs_sent, 0);
717 spin_lock(&tcon->stat_lock);
718 tcon->bytes_read = 0;
719 tcon->bytes_written = 0;
720 tcon->stats_from_time = ktime_get_real_seconds();
721 spin_unlock(&tcon->stat_lock);
722 if (server->ops->clear_stats)
723 server->ops->clear_stats(tcon);
724 }
725 }
726 }
727 spin_unlock(&cifs_tcp_ses_lock);
728 } else {
729 return rc;
730 }
731
732 return count;
733}
734
735static int cifs_stats_proc_show(struct seq_file *m, void *v)
736{
737 int i;
738#ifdef CONFIG_CIFS_STATS2
739 int j;
740#endif /* STATS2 */
741 struct TCP_Server_Info *server;
742 struct cifs_ses *ses;
743 struct cifs_tcon *tcon;
744
745 seq_printf(m, "Resources in use\nCIFS Session: %d\n",
746 sesInfoAllocCount.counter);
747 seq_printf(m, "Share (unique mount targets): %d\n",
748 tconInfoAllocCount.counter);
749 seq_printf(m, "SMB Request/Response Buffer: %d Pool size: %d\n",
750 buf_alloc_count.counter,
751 cifs_min_rcv + tcpSesAllocCount.counter);
752 seq_printf(m, "SMB Small Req/Resp Buffer: %d Pool size: %d\n",
753 small_buf_alloc_count.counter, cifs_min_small);
754#ifdef CONFIG_CIFS_STATS2
755 seq_printf(m, "Total Large %d Small %d Allocations\n",
756 atomic_read(&total_buf_alloc_count),
757 atomic_read(&total_small_buf_alloc_count));
758#endif /* CONFIG_CIFS_STATS2 */
759
760 seq_printf(m, "Operations (MIDs): %d\n", atomic_read(&mid_count));
761 seq_printf(m,
762 "\n%d session %d share reconnects\n",
763 tcpSesReconnectCount.counter, tconInfoReconnectCount.counter);
764
765 seq_printf(m,
766 "Total vfs operations: %d maximum at one time: %d\n",
767 GlobalCurrentXid, GlobalMaxActiveXid);
768
769 i = 0;
770 spin_lock(&cifs_tcp_ses_lock);
771 list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
772 seq_printf(m, "\nMax requests in flight: %d", server->max_in_flight);
773#ifdef CONFIG_CIFS_STATS2
774 seq_puts(m, "\nTotal time spent processing by command. Time ");
775 seq_printf(m, "units are jiffies (%d per second)\n", HZ);
776 seq_puts(m, " SMB3 CMD\tNumber\tTotal Time\tFastest\tSlowest\n");
777 seq_puts(m, " --------\t------\t----------\t-------\t-------\n");
778 for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++)
779 seq_printf(m, " %d\t\t%d\t%llu\t\t%u\t%u\n", j,
780 atomic_read(&server->num_cmds[j]),
781 server->time_per_cmd[j],
782 server->fastest_cmd[j],
783 server->slowest_cmd[j]);
784 for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++)
785 if (atomic_read(&server->smb2slowcmd[j])) {
786 spin_lock(&server->srv_lock);
787 seq_printf(m, " %d slow responses from %s for command %d\n",
788 atomic_read(&server->smb2slowcmd[j]),
789 server->hostname, j);
790 spin_unlock(&server->srv_lock);
791 }
792#endif /* STATS2 */
793 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
794 if (cifs_ses_exiting(ses))
795 continue;
796 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
797 i++;
798 seq_printf(m, "\n%d) %s", i, tcon->tree_name);
799 if (tcon->need_reconnect)
800 seq_puts(m, "\tDISCONNECTED ");
801 seq_printf(m, "\nSMBs: %d since %ptTs UTC",
802 atomic_read(&tcon->num_smbs_sent),
803 &tcon->stats_from_time);
804 if (server->ops->print_stats)
805 server->ops->print_stats(m, tcon);
806 }
807 }
808 }
809 spin_unlock(&cifs_tcp_ses_lock);
810
811 seq_putc(m, '\n');
812 return 0;
813}
814
815static int cifs_stats_proc_open(struct inode *inode, struct file *file)
816{
817 return single_open(file, cifs_stats_proc_show, NULL);
818}
819
820static const struct proc_ops cifs_stats_proc_ops = {
821 .proc_open = cifs_stats_proc_open,
822 .proc_read = seq_read,
823 .proc_lseek = seq_lseek,
824 .proc_release = single_release,
825 .proc_write = cifs_stats_proc_write,
826};
827
828#ifdef CONFIG_CIFS_SMB_DIRECT
829#define PROC_FILE_DEFINE(name) \
830static ssize_t name##_write(struct file *file, const char __user *buffer, \
831 size_t count, loff_t *ppos) \
832{ \
833 int rc; \
834 rc = kstrtoint_from_user(buffer, count, 10, &name); \
835 if (rc) \
836 return rc; \
837 return count; \
838} \
839static int name##_proc_show(struct seq_file *m, void *v) \
840{ \
841 seq_printf(m, "%d\n", name); \
842 return 0; \
843} \
844static int name##_open(struct inode *inode, struct file *file) \
845{ \
846 return single_open(file, name##_proc_show, NULL); \
847} \
848\
849static const struct proc_ops cifs_##name##_proc_fops = { \
850 .proc_open = name##_open, \
851 .proc_read = seq_read, \
852 .proc_lseek = seq_lseek, \
853 .proc_release = single_release, \
854 .proc_write = name##_write, \
855}
856
857PROC_FILE_DEFINE(rdma_readwrite_threshold);
858PROC_FILE_DEFINE(smbd_max_frmr_depth);
859PROC_FILE_DEFINE(smbd_keep_alive_interval);
860PROC_FILE_DEFINE(smbd_max_receive_size);
861PROC_FILE_DEFINE(smbd_max_fragmented_recv_size);
862PROC_FILE_DEFINE(smbd_max_send_size);
863PROC_FILE_DEFINE(smbd_send_credit_target);
864PROC_FILE_DEFINE(smbd_receive_credit_max);
865#endif
866
867static struct proc_dir_entry *proc_fs_cifs;
868static const struct proc_ops cifsFYI_proc_ops;
869static const struct proc_ops cifs_lookup_cache_proc_ops;
870static const struct proc_ops traceSMB_proc_ops;
871static const struct proc_ops cifs_security_flags_proc_ops;
872static const struct proc_ops cifs_linux_ext_proc_ops;
873static const struct proc_ops cifs_mount_params_proc_ops;
874
875void
876cifs_proc_init(void)
877{
878 proc_fs_cifs = proc_mkdir("fs/cifs", NULL);
879 if (proc_fs_cifs == NULL)
880 return;
881
882 proc_create_single("DebugData", 0, proc_fs_cifs,
883 cifs_debug_data_proc_show);
884
885 proc_create_single("open_files", 0400, proc_fs_cifs,
886 cifs_debug_files_proc_show);
887
888 proc_create_single("open_dirs", 0400, proc_fs_cifs,
889 cifs_debug_dirs_proc_show);
890
891 proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_ops);
892 proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_ops);
893 proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_ops);
894 proc_create("LinuxExtensionsEnabled", 0644, proc_fs_cifs,
895 &cifs_linux_ext_proc_ops);
896 proc_create("SecurityFlags", 0644, proc_fs_cifs,
897 &cifs_security_flags_proc_ops);
898 proc_create("LookupCacheEnabled", 0644, proc_fs_cifs,
899 &cifs_lookup_cache_proc_ops);
900
901 proc_create("mount_params", 0444, proc_fs_cifs, &cifs_mount_params_proc_ops);
902
903#ifdef CONFIG_CIFS_DFS_UPCALL
904 proc_create("dfscache", 0644, proc_fs_cifs, &dfscache_proc_ops);
905#endif
906
907#ifdef CONFIG_CIFS_SMB_DIRECT
908 proc_create("rdma_readwrite_threshold", 0644, proc_fs_cifs,
909 &cifs_rdma_readwrite_threshold_proc_fops);
910 proc_create("smbd_max_frmr_depth", 0644, proc_fs_cifs,
911 &cifs_smbd_max_frmr_depth_proc_fops);
912 proc_create("smbd_keep_alive_interval", 0644, proc_fs_cifs,
913 &cifs_smbd_keep_alive_interval_proc_fops);
914 proc_create("smbd_max_receive_size", 0644, proc_fs_cifs,
915 &cifs_smbd_max_receive_size_proc_fops);
916 proc_create("smbd_max_fragmented_recv_size", 0644, proc_fs_cifs,
917 &cifs_smbd_max_fragmented_recv_size_proc_fops);
918 proc_create("smbd_max_send_size", 0644, proc_fs_cifs,
919 &cifs_smbd_max_send_size_proc_fops);
920 proc_create("smbd_send_credit_target", 0644, proc_fs_cifs,
921 &cifs_smbd_send_credit_target_proc_fops);
922 proc_create("smbd_receive_credit_max", 0644, proc_fs_cifs,
923 &cifs_smbd_receive_credit_max_proc_fops);
924#endif
925}
926
927void
928cifs_proc_clean(void)
929{
930 if (proc_fs_cifs == NULL)
931 return;
932
933 remove_proc_entry("DebugData", proc_fs_cifs);
934 remove_proc_entry("open_files", proc_fs_cifs);
935 remove_proc_entry("open_dirs", proc_fs_cifs);
936 remove_proc_entry("cifsFYI", proc_fs_cifs);
937 remove_proc_entry("traceSMB", proc_fs_cifs);
938 remove_proc_entry("Stats", proc_fs_cifs);
939 remove_proc_entry("SecurityFlags", proc_fs_cifs);
940 remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs);
941 remove_proc_entry("LookupCacheEnabled", proc_fs_cifs);
942 remove_proc_entry("mount_params", proc_fs_cifs);
943
944#ifdef CONFIG_CIFS_DFS_UPCALL
945 remove_proc_entry("dfscache", proc_fs_cifs);
946#endif
947#ifdef CONFIG_CIFS_SMB_DIRECT
948 remove_proc_entry("rdma_readwrite_threshold", proc_fs_cifs);
949 remove_proc_entry("smbd_max_frmr_depth", proc_fs_cifs);
950 remove_proc_entry("smbd_keep_alive_interval", proc_fs_cifs);
951 remove_proc_entry("smbd_max_receive_size", proc_fs_cifs);
952 remove_proc_entry("smbd_max_fragmented_recv_size", proc_fs_cifs);
953 remove_proc_entry("smbd_max_send_size", proc_fs_cifs);
954 remove_proc_entry("smbd_send_credit_target", proc_fs_cifs);
955 remove_proc_entry("smbd_receive_credit_max", proc_fs_cifs);
956#endif
957 remove_proc_entry("fs/cifs", NULL);
958}
959
960static int cifsFYI_proc_show(struct seq_file *m, void *v)
961{
962 seq_printf(m, "%d\n", cifsFYI);
963 return 0;
964}
965
966static int cifsFYI_proc_open(struct inode *inode, struct file *file)
967{
968 return single_open(file, cifsFYI_proc_show, NULL);
969}
970
971static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer,
972 size_t count, loff_t *ppos)
973{
974 char c[2] = { '\0' };
975 bool bv;
976 int rc;
977
978 rc = get_user(c[0], buffer);
979 if (rc)
980 return rc;
981 if (kstrtobool(c, &bv) == 0)
982 cifsFYI = bv;
983 else if ((c[0] > '1') && (c[0] <= '9'))
984 cifsFYI = (int) (c[0] - '0'); /* see cifs_debug.h for meanings */
985 else
986 return -EINVAL;
987
988 return count;
989}
990
991static const struct proc_ops cifsFYI_proc_ops = {
992 .proc_open = cifsFYI_proc_open,
993 .proc_read = seq_read,
994 .proc_lseek = seq_lseek,
995 .proc_release = single_release,
996 .proc_write = cifsFYI_proc_write,
997};
998
999static int cifs_linux_ext_proc_show(struct seq_file *m, void *v)
1000{
1001 seq_printf(m, "%d\n", linuxExtEnabled);
1002 return 0;
1003}
1004
1005static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file)
1006{
1007 return single_open(file, cifs_linux_ext_proc_show, NULL);
1008}
1009
1010static ssize_t cifs_linux_ext_proc_write(struct file *file,
1011 const char __user *buffer, size_t count, loff_t *ppos)
1012{
1013 int rc;
1014
1015 rc = kstrtobool_from_user(buffer, count, &linuxExtEnabled);
1016 if (rc)
1017 return rc;
1018
1019 return count;
1020}
1021
1022static const struct proc_ops cifs_linux_ext_proc_ops = {
1023 .proc_open = cifs_linux_ext_proc_open,
1024 .proc_read = seq_read,
1025 .proc_lseek = seq_lseek,
1026 .proc_release = single_release,
1027 .proc_write = cifs_linux_ext_proc_write,
1028};
1029
1030static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v)
1031{
1032 seq_printf(m, "%d\n", lookupCacheEnabled);
1033 return 0;
1034}
1035
1036static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file)
1037{
1038 return single_open(file, cifs_lookup_cache_proc_show, NULL);
1039}
1040
1041static ssize_t cifs_lookup_cache_proc_write(struct file *file,
1042 const char __user *buffer, size_t count, loff_t *ppos)
1043{
1044 int rc;
1045
1046 rc = kstrtobool_from_user(buffer, count, &lookupCacheEnabled);
1047 if (rc)
1048 return rc;
1049
1050 return count;
1051}
1052
1053static const struct proc_ops cifs_lookup_cache_proc_ops = {
1054 .proc_open = cifs_lookup_cache_proc_open,
1055 .proc_read = seq_read,
1056 .proc_lseek = seq_lseek,
1057 .proc_release = single_release,
1058 .proc_write = cifs_lookup_cache_proc_write,
1059};
1060
1061static int traceSMB_proc_show(struct seq_file *m, void *v)
1062{
1063 seq_printf(m, "%d\n", traceSMB);
1064 return 0;
1065}
1066
1067static int traceSMB_proc_open(struct inode *inode, struct file *file)
1068{
1069 return single_open(file, traceSMB_proc_show, NULL);
1070}
1071
1072static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer,
1073 size_t count, loff_t *ppos)
1074{
1075 int rc;
1076
1077 rc = kstrtobool_from_user(buffer, count, &traceSMB);
1078 if (rc)
1079 return rc;
1080
1081 return count;
1082}
1083
1084static const struct proc_ops traceSMB_proc_ops = {
1085 .proc_open = traceSMB_proc_open,
1086 .proc_read = seq_read,
1087 .proc_lseek = seq_lseek,
1088 .proc_release = single_release,
1089 .proc_write = traceSMB_proc_write,
1090};
1091
1092static int cifs_security_flags_proc_show(struct seq_file *m, void *v)
1093{
1094 seq_printf(m, "0x%x\n", global_secflags);
1095 return 0;
1096}
1097
1098static int cifs_security_flags_proc_open(struct inode *inode, struct file *file)
1099{
1100 return single_open(file, cifs_security_flags_proc_show, NULL);
1101}
1102
1103/*
1104 * Ensure that if someone sets a MUST flag, that we disable all other MAY
1105 * flags except for the ones corresponding to the given MUST flag. If there are
1106 * multiple MUST flags, then try to prefer more secure ones.
1107 */
1108static void
1109cifs_security_flags_handle_must_flags(unsigned int *flags)
1110{
1111 unsigned int signflags = *flags & (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL);
1112
1113 if ((*flags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
1114 *flags = CIFSSEC_MUST_KRB5;
1115 else if ((*flags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
1116 *flags = CIFSSEC_MUST_NTLMSSP;
1117 else if ((*flags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
1118 *flags = CIFSSEC_MUST_NTLMV2;
1119
1120 *flags |= signflags;
1121}
1122
1123static ssize_t cifs_security_flags_proc_write(struct file *file,
1124 const char __user *buffer, size_t count, loff_t *ppos)
1125{
1126 int rc;
1127 unsigned int flags;
1128 char flags_string[12];
1129 bool bv;
1130
1131 if ((count < 1) || (count > 11))
1132 return -EINVAL;
1133
1134 memset(flags_string, 0, sizeof(flags_string));
1135
1136 if (copy_from_user(flags_string, buffer, count))
1137 return -EFAULT;
1138
1139 if (count < 3) {
1140 /* single char or single char followed by null */
1141 if (kstrtobool(flags_string, &bv) == 0) {
1142 global_secflags = bv ? CIFSSEC_MAX : CIFSSEC_DEF;
1143 return count;
1144 } else if (!isdigit(flags_string[0])) {
1145 cifs_dbg(VFS, "Invalid SecurityFlags: %s\n",
1146 flags_string);
1147 return -EINVAL;
1148 }
1149 }
1150
1151 /* else we have a number */
1152 rc = kstrtouint(flags_string, 0, &flags);
1153 if (rc) {
1154 cifs_dbg(VFS, "Invalid SecurityFlags: %s\n",
1155 flags_string);
1156 return rc;
1157 }
1158
1159 cifs_dbg(FYI, "sec flags 0x%x\n", flags);
1160
1161 if (flags == 0) {
1162 cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", flags_string);
1163 return -EINVAL;
1164 }
1165
1166 if (flags & ~CIFSSEC_MASK) {
1167 cifs_dbg(VFS, "Unsupported security flags: 0x%x\n",
1168 flags & ~CIFSSEC_MASK);
1169 return -EINVAL;
1170 }
1171
1172 cifs_security_flags_handle_must_flags(&flags);
1173
1174 /* flags look ok - update the global security flags for cifs module */
1175 global_secflags = flags;
1176 if (global_secflags & CIFSSEC_MUST_SIGN) {
1177 /* requiring signing implies signing is allowed */
1178 global_secflags |= CIFSSEC_MAY_SIGN;
1179 cifs_dbg(FYI, "packet signing now required\n");
1180 } else if ((global_secflags & CIFSSEC_MAY_SIGN) == 0) {
1181 cifs_dbg(FYI, "packet signing disabled\n");
1182 }
1183 /* BB should we turn on MAY flags for other MUST options? */
1184 return count;
1185}
1186
1187static const struct proc_ops cifs_security_flags_proc_ops = {
1188 .proc_open = cifs_security_flags_proc_open,
1189 .proc_read = seq_read,
1190 .proc_lseek = seq_lseek,
1191 .proc_release = single_release,
1192 .proc_write = cifs_security_flags_proc_write,
1193};
1194
1195/* To make it easier to debug, can help to show mount params */
1196static int cifs_mount_params_proc_show(struct seq_file *m, void *v)
1197{
1198 const struct fs_parameter_spec *p;
1199 const char *type;
1200
1201 for (p = smb3_fs_parameters; p->name; p++) {
1202 /* cannot use switch with pointers... */
1203 if (!p->type) {
1204 if (p->flags == fs_param_neg_with_no)
1205 type = "noflag";
1206 else
1207 type = "flag";
1208 } else if (p->type == fs_param_is_bool)
1209 type = "bool";
1210 else if (p->type == fs_param_is_u32)
1211 type = "u32";
1212 else if (p->type == fs_param_is_u64)
1213 type = "u64";
1214 else if (p->type == fs_param_is_string)
1215 type = "string";
1216 else
1217 type = "unknown";
1218
1219 seq_printf(m, "%s:%s\n", p->name, type);
1220 }
1221
1222 return 0;
1223}
1224
1225static int cifs_mount_params_proc_open(struct inode *inode, struct file *file)
1226{
1227 return single_open(file, cifs_mount_params_proc_show, NULL);
1228}
1229
1230static const struct proc_ops cifs_mount_params_proc_ops = {
1231 .proc_open = cifs_mount_params_proc_open,
1232 .proc_read = seq_read,
1233 .proc_lseek = seq_lseek,
1234 .proc_release = single_release,
1235 /* No need for write for now */
1236 /* .proc_write = cifs_mount_params_proc_write, */
1237};
1238
1239#else
1240void cifs_proc_init(void)
1241{
1242}
1243
1244void cifs_proc_clean(void)
1245{
1246}
1247#endif /* PROC_FS */