Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cifs: client: enforce consistent handling of multichannel and max_channels

Previously, the behavior of the multichannel and max_channels mount
options was inconsistent and order-dependent. For example, specifying
"multichannel,max_channels=1" would result in 2 channels, while
"max_channels=1,multichannel" would result in 1 channel. Additionally,
conflicting combinations such as "nomultichannel,max_channels=3" or
"multichannel,max_channels=1" did not produce errors and could lead to
unexpected channel counts.

This commit introduces two new fields in smb3_fs_context to explicitly
track whether multichannel and max_channels were specified during
mount. The option parsing and validation logic is updated to ensure:
- The outcome is no longer dependent on the order of options.
- Conflicting combinations (e.g., "nomultichannel,max_channels=3" or
"multichannel,max_channels=1") are detected and result in an error.
- The number of channels created is consistent with the specified
options.

This improves the reliability and predictability of mount option
handling for SMB3 multichannel support.

Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Rajasi Mandal <rajasimandal@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Rajasi Mandal and committed by
Steve French
1ef15fbe 86973754

+50 -18
-1
fs/smb/client/cifsfs.c
··· 1016 1016 } else { 1017 1017 cifs_info("Attempting to mount %s\n", old_ctx->source); 1018 1018 } 1019 - 1020 1019 cifs_sb = kzalloc(sizeof(*cifs_sb), GFP_KERNEL); 1021 1020 if (!cifs_sb) 1022 1021 return ERR_PTR(-ENOMEM);
+48 -17
fs/smb/client/fs_context.c
··· 711 711 return 0; 712 712 } 713 713 714 + static int smb3_handle_conflicting_options(struct fs_context *fc) 715 + { 716 + struct smb3_fs_context *ctx = smb3_fc2context(fc); 717 + 718 + if (ctx->multichannel_specified) { 719 + if (ctx->multichannel) { 720 + if (!ctx->max_channels_specified) { 721 + ctx->max_channels = 2; 722 + } else if (ctx->max_channels == 1) { 723 + cifs_errorf(fc, 724 + "max_channels must be greater than 1 when multichannel is enabled\n"); 725 + return -EINVAL; 726 + } 727 + } else { 728 + if (!ctx->max_channels_specified) { 729 + ctx->max_channels = 1; 730 + } else if (ctx->max_channels > 1) { 731 + cifs_errorf(fc, 732 + "max_channels must be equal to 1 when multichannel is disabled\n"); 733 + return -EINVAL; 734 + } 735 + } 736 + } else { 737 + if (ctx->max_channels_specified) { 738 + if (ctx->max_channels > 1) 739 + ctx->multichannel = true; 740 + else 741 + ctx->multichannel = false; 742 + } else { 743 + ctx->multichannel = false; 744 + ctx->max_channels = 1; 745 + } 746 + } 747 + 748 + //resetting default values as remount doesn't initialize fs_context again 749 + ctx->multichannel_specified = false; 750 + ctx->max_channels_specified = false; 751 + 752 + return 0; 753 + } 754 + 714 755 static void smb3_fs_context_free(struct fs_context *fc); 715 756 static int smb3_fs_context_parse_param(struct fs_context *fc, 716 757 struct fs_parameter *param); ··· 825 784 if (ret < 0) 826 785 break; 827 786 } 787 + ret = smb3_handle_conflicting_options(fc); 828 788 829 789 return ret; 830 790 } ··· 1292 1250 ctx->nodelete = 1; 1293 1251 break; 1294 1252 case Opt_multichannel: 1295 - if (result.negated) { 1253 + ctx->multichannel_specified = true; 1254 + if (result.negated) 1296 1255 ctx->multichannel = false; 1297 - ctx->max_channels = 1; 1298 - } else { 1256 + else 1299 1257 ctx->multichannel = true; 1300 - /* if number of channels not specified, default to 2 */ 1301 - if (ctx->max_channels < 2) 1302 - ctx->max_channels = 2; 1303 - } 1304 1258 break; 1305 1259 case Opt_uid: 1306 1260 ctx->linux_uid = result.uid; ··· 1432 1394 ctx->max_credits = result.uint_32; 1433 1395 break; 1434 1396 case Opt_max_channels: 1397 + ctx->max_channels_specified = true; 1435 1398 if (result.uint_32 < 1 || result.uint_32 > CIFS_MAX_CHANNELS) { 1436 1399 cifs_errorf(fc, "%s: Invalid max_channels value, needs to be 1-%d\n", 1437 1400 __func__, CIFS_MAX_CHANNELS); 1438 1401 goto cifs_parse_mount_err; 1439 1402 } 1440 1403 ctx->max_channels = result.uint_32; 1441 - /* If more than one channel requested ... they want multichan */ 1442 - if (result.uint_32 > 1) 1443 - ctx->multichannel = true; 1444 1404 break; 1445 1405 case Opt_max_cached_dirs: 1446 1406 if (result.uint_32 < 1) { ··· 1856 1820 goto cifs_parse_mount_err; 1857 1821 } 1858 1822 1859 - /* 1860 - * Multichannel is not meaningful if max_channels is 1. 1861 - * Force multichannel to false to ensure consistent configuration. 1862 - */ 1863 - if (ctx->multichannel && ctx->max_channels == 1) 1864 - ctx->multichannel = false; 1865 - 1866 1823 return 0; 1867 1824 1868 1825 cifs_parse_mount_err: ··· 1942 1913 1943 1914 /* default to no multichannel (single server connection) */ 1944 1915 ctx->multichannel = false; 1916 + ctx->multichannel_specified = false; 1917 + ctx->max_channels_specified = false; 1945 1918 ctx->max_channels = 1; 1946 1919 1947 1920 ctx->backupuid_specified = false; /* no backup intent for a user */
+2
fs/smb/client/fs_context.h
··· 294 294 bool domainauto:1; 295 295 bool rdma:1; 296 296 bool multichannel:1; 297 + bool multichannel_specified:1; /* true if user specified multichannel or nomultichannel */ 298 + bool max_channels_specified:1; /* true if user specified max_channels */ 297 299 bool use_client_guid:1; 298 300 /* reuse existing guid for multichannel */ 299 301 u8 client_guid[SMB2_CLIENT_GUID_SIZE];