Mounting network share

Has anyone been able to get a network share mounted on the Jetson Nano? I have followed the two threads that come up here, and neither seems to have a real solution to the issue (although it’s encouraging that it has worked at one point in time). The error that I am getting is a missing UTF-8 kernel module

CIFS VFS: CIFS mount error: iocharset utf8 not found

If I don’t specify iocharset if fstab, it gives a different error:

CIFS VFS: cifs_mount failed w/return code = -95

I have installed cifs-utils, samba, smbclient, and a lot of other things.

A little research on the first error let me to an issue that a specific kernel module (nls_utf8.ko, I believe). I spent a lot of time trying to compile this kernel module without much success (got as far as creating a .o file, but can’t get a .ko). I definitely don’t know what I’m doing, trying to build a kernel module.

Is there anyone who has been able to mount a network share?

You are correct that you need the kernel module. If the kernel were stock, then you could do this (but it won’t work on a Jetson):
sudo apt-get install "linux-kernel-modules-extra-$(uname -r)"
…and the reason this is unlikely to work is that the kernel version and its “uname -r” are not normally available from stock Ubuntu repositories. I suppose if NVIDIA built these and put them on its server, then it would work.

The whole reason this is needed is that there are a lot of different character sets out in the wild, and normally only some default is installed. For example, not many people will end up with Sanskrit, so why install it if not using it?

So you do need to custom compile this module. The reason you are probably having trouble is that the source code configuration must match your running system in order to use the module with your system.

There are several places describing kernel module build, but getting configuration correct is not always obvious. For example, the official docs on cross compiling are thorough if you are installing everything, but not so obvious if you are making just a module to match a given kernel. So here are some random facts to help (random because you will probably need to know this, but the location and timing of using the information varies):

  • When you “make tegra_defconfig” you are configuring the kernel. That configuration is a sane default and mostly matches any unmodified default install. However, you still need to take into account “CONFIG_LOCALVERSION”.
  • Any configuration created from a “make tegra_defconfig” is reflected in the “.config” file of the root of the kernel source.
  • You are better off creating your default “.config” by asking the Jetson what its actual config is. If there are any changes, then your config will be an exact match which tegra_defconfig won’t catch. You still need to edit “CONFIG_LOCALVERSION”, and this is true regardless of whether you compile based on tegra_defconfig or asking the running Jetson what its config is. To get this configuration you would copy “/proc/config.gz” somewhere safe (keep a copy), gunzip config.gz", edit “CONFIG_LOCALVERSION” to:
    CONFIG_LOCALVERSION="-tegra"
    …and then copy this to your kernel build location with name “.config”.
  • Even if you build only a module it is a good “sanity check” to build the “Image” target first. There are shortcuts to propagating configuration when building just modules, but I recommend not using those shortcuts until after the “Image” (full kernel) target has been built once.

So the short list is:

  • Get the official docs on kernel build.
  • Build the base kernel Image even if you don’t need to. You could skip this and instead shorten time of build with “make modules_prepare” prior to building modules, but if you do so, then you lack one sanity test of your configuration.
  • Place a copy of the gunzip’d “/proc/config.gz” on your host, and skip the “make tegra_defconfig”. Copy the gunzip’d and edited (for “CONFIG_LOCALVERSION”) in your output build directory for the kernel build (probably docs will mention setting an environment variable “TEGRA_KERNEL_OUT”…this is the build location, and is different than the source location).
  • Either “make Image” or “make modules_prepare” (both will propagate config for module build). “Image” is preferred since failures will show up. Only needs to be done once.
  • make modules”.

After you build, at your “$TEGRA_KERNEL_OUT” location, there will be a subdirectory:
fs/nls/
…within this there will be content for each module built, and in your case (probably…I didn’t actually build this):
fs/nls/nls_utf8.ko

BIG note: Everything I’ve mentioned already has been for building an exact match to your running system. You still need to edit the config to build “nls_utf8.ko”. I recommend using “make nconfig” (after setting up the matching “.config”), but most docs will show using “make menuconfig”. The two commands will require first “sudo apt-get install ncurses5-dev”. I use “nconfig” because it has a “symbol search” function. You could for example search for “utf8”, and then enable this in the form of a module (the “m” key for selection in “make nconfig” or “make menuconfig”. This simplifies using the editor to find particular features. Once this is done you can perform the actual compile. Any “Image” created would be an exact match to your running system, and any modules created would be an exact match with the exception of now also having the nls_utf feature as a module.

You can always ask more questions, but you can get started if you go to the docs for your specific L4T release and then looking at “kernel customization”. Your L4T release can be found by “head -n 1 /etc/nv_tegra_release”. The URL where you can find each L4T release (and then navigate to its docs) is here:
https://developer.nvidia.com/linux-tegra

Btw, it is actually much simpler in practice if you’ve done this once. Don’t let it scare you. The module install is really just a copy of a single file once done. I’ve added a lot of details based on what tends to fail based on tiny details.

Also, official docs will be for cross compile…building on the host PC for install to the Jetson. There are several environment variables mentioned there…you don’t need those for a native compile, but do for cross compile. If I’ve mentioned “make tegra_defconfig”, then this is the same (other than environment) as what the docs will show as “make O=$TEGRA_KERNEL_OUT tegra_defconfig”. The environment variables are just for finding things or for keeping clutter of temporary content someone other than in the pristine source code. Feel free to ask questions.

1 Like

Thanks for the detailed instructions, I think it is starting to make sense to me. Originally I was just trying to build on the NANO, since I was afraid of messing up my primary system. In the end though, I decided to just cross-compile, and I followed the detailed instructions here:
https://developer.ridgerun.com/wiki/index.php?title=Jetson_Nano/Development/Building_the_Kernel_from_Source

I could not get the compile to work from the modified .config from the Nano, but that is really just a fresh install so (I’m hoping) it shouldn’t be too different. So, I did the following, slightly modified to do menuconfig:

cd $JETSON_NANO_KERNEL_SOURCE
TOOLCHAIN_PREFIX=$HOME/l4t-gcc/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
TEGRA_KERNEL_OUT=$JETSON_NANO_KERNEL_SOURCE/build
KERNEL_MODULES_OUT=$JETSON_NANO_KERNEL_SOURCE/modules
make -C kernel/kernel-4.9/ ARCH=arm64 O=$TEGRA_KERNEL_OUT LOCALVERSION=-tegra CROSS_COMPILE=${TOOLCHAIN_PREFIX} tegra_defconfig
cd build
make oldconfig
make nconfig
(add the UTF-8 module from File Systems/Native Language Support/NLS UTF-8, and safe the new configuration in .config)
make -C kernel/kernel-4.9/ ARCH=arm64 O=$TEGRA_KERNEL_OUT LOCALVERSION=-tegra CROSS_COMPILE=${TOOLCHAIN_PREFIX} -j8 --output-sync=target zImage
make -C kernel/kernel-4.9/ ARCH=arm64 O=$TEGRA_KERNEL_OUT LOCALVERSION=-tegra CROSS_COMPILE=${TOOLCHAIN_PREFIX} -j8 --output-sync=target modules
make -C kernel/kernel-4.9/ ARCH=arm64 O=$TEGRA_KERNEL_OUT LOCALVERSION=-tegra CROSS_COMPILE=${TOOLCHAIN_PREFIX} -j8 --output-sync=target dtbs
make -C kernel/kernel-4.9/ ARCH=arm64 O=$TEGRA_KERNEL_OUT LOCALVERSION=-tegra INSTALL_MOD_PATH=$KERNEL_MODULES_OUT modules_install

Everything seems to go fine, no errors that I noticed, but unfortunately I still only get the nls_utf8.o file, not the nls_utf8.ko. Any ideas on what I did wrong? I did confirm the .config file seems to have the right setting by running cat .config | grep NLS, which shows the following:
CONFIG_NLS_UTF8=y

I often build directly on the system, but you have to be very careful since running out of disk space is quite easy to do. One would have to set up some sort of secondary disk space to guarantee not doing so.

Was that “.config” from “/proc/config.gz”, edited for CONFIG_LOCALVERSION? Or was it from the “make tegra_defconfig” target? Either should work.

…this is not recommended to build temporary output into the actual kernel source tree. Having a separate build directory does work around the flaws in this, but if your kernel source is in a root owned read-only location, then it means you’d have to use sudo to compile. This has a number of risks. It would be better if you named some temporary location from your user, e.g., “~/build”. Still, it will work to do as you mention.

Same story for module output locations. Perhaps you could:

cd ~
mkdir kernel
cd kernel
mkdir build
mkdir modules
cd ~/kernel/build
# pwd just echos your current location/directory. The surrounding back quotes turn it into a substitution macro.
export TEGRA_KERNEL_OUT=`pwd`
cd ../modules
export KERNEL_MODULES_OUT=`pwd`
cd $JETSON_KERNEL_SOURCE
# ...do the build...

Now I see the answer I missed before, that the tegra_defconfig is what you used. This should work fine as a base config, but it isn’t as foolproof as using the “/proc/config.gz” if things have changed. Your use of the config editor was correct (assuming you named the “TEGRA_KERNEL_OUT” during the use of the configuration editor…without that the editor would not find the correct “.config” location).

Don’t cd to the build location. Stay in the kernel source parent directory. By using the environment which points at an alternate temporary output (“TEGRA_KERNEL_OUT”) your original source will be unmodified and all output will use the .config at TEGRA_KERNEL_OUT. If root owns your entire kernel source tree, and you do this with a regular user, then you using best practice. If you have ever modifed the kernel source without knowing it, even if it is just a config detail, then very little will work like you expect it to. Always leave the kernel source owned by root and non-modifiable by any other user. Always put your temporary output location where your non-root build user has write permission.

FYI, I don’t know if your “make” commands above (e.g., the “make nconfig”) used the “O=$TEGRA_KERNEL_OUT” or not, but if you did not use that during any step, then the wrong content was used. Did you literally use “make nconfig”? Or did you use “make O=$TEGRA_KERNEL_OUT nconfig”? If building in an alternate location, then all commands must be told to do so.

I have not verified that CONFIG_NLS_UTF8 creates that feature, but if you used “=y” instead of “=m”, then you told it to not create a module. The content would be placed directly in the main kernel Image file. You would want to use “make O=$TEGRA_KERNEL_OUT nconfig", and set this to "=m`”. It is far simpler and far less risky to build and use this as a module than it is to replace the entire kernel and all modules.

So it turns out, the problem was that I was using the wrong option in nconfig (* rather than M), so I do now have the .ko file. How do I load it into the kernel on the nano? I tried just creating the nls directory and putting the ko file there, but it still gives the missing UTF-8 error.

UPDATE:
I tried using insmod, and I get an error that the module has an invalid module format. I’m guessing that means that I really did need to start from the config file on the nano. Or alternatively, I need to put this module into the default kernel and reinstall the OS. I’m thinking the latter will be easiest.

UPDATE 2:
It seems that my kernel source does not match what is actually on the nano. Somehow I ended up with kernel version 4.9.201-tegra, but the source is 4.9.140-tegra. I’m trying to find the correct source code for the updated kernel, so far it doesn’t appear to be in the downloads.

I did eventually get the module compiled (it helps to have the right kernel version). In case anyone is looking, the kernel source is here:
https://developer.nvidia.com/embedded/L4T/r32_Release_v5.0/sources/T186/public_sources.tbz2

Running insmod with this ko file appears to work without errors. But unfortunately after all that, I am still getting the same missing UTF-8 error. Maybe I still didn’t insert it correctly. The config.gz file does not reflect that it is loaded.

Sounds like you got past some of the issues, but for those it might help, the location of the subdirectory in the kernel source where a given module is produced is a mirror to the base of the modules: “/lib/modules/$(uname -r)/kernel”.

So for example, if you found ‘nls_utf8.ko’ in the source tree subdirectory “fs/nls/”, then you would copy the module to “/lib/modules/$(uname -r)/kernel/fs/nls/nls_utf8.ko”.

Whenever you see an invalid module format, then it probably means you compiled on a PC but did not set up the environment for cross compile to arm64. Think of the environment variable for the toolchain in combination with the environment variable for “ARCH=arm64”.

After you’ve installed a module you might consider either rebooting or “sudo depmod -a” in addition to the insmod.

If you run this command and see the nls_utf8 module listed, then you’ll know the module is correctly installed: “lsmod”.

lsmod is key to seeing if it is there.

The config.gz would reflect the “=m” or “=y” if and only if that was selected at the time of kernel build for the kernel Image file. If you reuse an older Image file, then I believe the “=m” won’t necessarily show up…it would show up if you had also replaced the actual Image. “config.gz” is a reflection of the configuration used at the moment that this running kernel was built.

If you can verify that the “nls_utf.ko” was loaded, then it would be useful to see the error messages. Perhaps this module was needed, but might be only one of multiple dependencies.

It appears the module is loaded:

jetson1@jetson1:~$ modinfo nls_utf8.ko
filename:       /home/jetson1/nls_utf8.ko
license:        Dual BSD/GPL
depends:        
intree:         Y
vermagic:       4.9.201-tegra SMP preempt mod_unload modversions aarch64

This is the error that I get during startup, trying to mount the network share:
jetson1@jetson1:~$ dmesg | grep utf8
[ 162.981085] CIFS VFS: CIFS mount error: iocharset utf8 not found

And if I try to display the contents of the mount point for the network share folder, it says the following:
jetson1@jetson1:~$ ls /Misc
ls: cannot open directory ‘/Misc’: No such device

lsmod” has the advantage of listing dependencies. What do you see from “lsmod”?

Do you have your module located here?
/lib/modules/$(uname -r)/kernel/fs/nls/nls_utf8.ko

Also, here is a similar older thread you might want to use as a checklist:
https://forums.developer.nvidia.com/t/problem-mounting-drive-in-stab/156204/2

As for “ls /Misc”, if this is a mount point, then it must exist before anything can mount to it. Prior to attempting the mount, does “ls /Misc” still say it does not exist? If not, then this is a problem, and you could create this via “sudo mkdir /Misc”.

If I do lsmod, I just get the following:
jetson1@jetson1:~$ lsmod nls_utf8.ko
Usage: lsmod

The module is indeed located there, although I put it there myself prior to loading the module, is that problematic? I wasn’t sure if inserting the module copies it anywhere.

The mount point does exist as a directory, prior to mounting I manually created it. I also did chmod 777 and chown jetson1. Does the owner matter for mounting? I had run into problems in the past with not being able to make changes, and it seemed to help if the folder owner was the local user.

The error that I’m getting is different than when the directory doesn’t exist. For example, if I try to show the contents of something that doesn’t exist, it says no such file or directory, rather than the error above which is “No such device”. I will say, the network share works on other computers on the network, my raspberry pi sees it without problems.

As for the link, that thread was what led me to compiling the nls_utf8 module in the first place. The things that I’ve checked:

I had installed cifs-utils initially, but I did an apt purge and reinstall after adding the kernel module, just in case there might be some config file that needed to see the UTF8 module present. Is there anything else that I can do to help CIFS recognize the UTF-8 module?

Checked the local language:
jetson1@jetson1:~$ echo $LANG
en_US.UTF-8

I’m not sure what else to try.

I still don’t know what the answer is to use an SMB share, but I did find a workaround, to use an NFS share. This is, I guess, something I should have known all along, but if you only want to share files with other linux computers, apparently you don’t need samba, you can just use NFS. This works right out of the box on jetson nano, so I don’t need to bother with the samba share.

Don’t name the module. Just “lsmod”, literally. If the module is in place, then when needed, it should load. The command lsmod would tell us if it did load.

Owner does matter sometimes for mounting, but if the person doing the mounting is root (or uses “sudo”), then mount should work regardless of owner or permissions. The earlier messages tended to say the mount point did not exist, and this is a different issue. When there is more than one filesystem storage mechanism (e.g., both eMMC and USB or SD card), then it might be the mount point does not exist on the device the mount was attempted on even if it exists on one of the other devices.

If you ask to list a file or directory, then it is the filesystem missing something, and the message is different than a filesystem failure versus a mount failure. If this is a network share, then it isn’t set up correctly and would be the reason why something is missing. However, it seems you are attempting to mount a network share and you were (at least in the past) failing due to the nls_utf8 issue. Once that is cleared up, then there might be other issues (e.g., if you got past the nls_utf8 issue, then finding the mount point could be the next issue). You might run “dmesg --follow” just before attempting the mount, and see if logging is added during the mount attempt.

I don’t know about CIFS, You probably also need this. In fact it is probably CIFS which was in need of nls_utf8. CIFS is a network filesystem, and while in operation, it likely needs to know about which character set to use…which is an nls_utf8 thing. There could also be other CIFS or network filesystem related programs in need. You should probably go back to the documents and look to see what is required, and if the item is missing, add it back in. Should something be missing, then there is no way to help it.

I myself prefer NFS. You could use this temporarily until you solve the CIFS/nls_utf8 issues.