// Main maut ko takiya, aur kafan ko chaadar banakar audhta hoon!

#include <bits/stdc++.h>
#include <ext/pb_ds/detail/standard_policies.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

using namespace std;
using namespace __gnu_pbds;

using ll = long long;
using ld = long double;

#define pb emplace_back
#define push emplace
#define mp make_pair
#define ff first
#define ss second
#define all(v) v.begin(), v.end()
#define len(a) (int) a.size()
#define reset(a, val) memset(a, val, sizeof(a))
// setbase - cout << setbase (16); cout << 100 << endl; Prints 64
// setfill - cout << setfill ('x') << setw (5); cout << 77 << endl; prints xxx77
#define int long long
#define vll vector<int>
#define pll pair<int, int>
#define vvll vector<vector<int>>
#define vpll vector<pair<int, int>>
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
// shuffle(a.begin(), a.end(), rng) or shuffle(a, a+n, rng) instead of random_shuffle
// uniform_int_distribution<int/ld>(l, r)(rng) for generating random integer/double numbers in [l, r]

#ifdef ONLINE_JUDGE
	#define endl '\n'
	#pragma comment(linker, "/stack:268435456")
	#pragma GCC optimize ("O3")
	#pragma GCC optimize ("O2")
	#pragma GCC optimize("Ofast")
	#pragma GCC optimize ("unroll-loops")
	#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
	#define _CRT_SECURE_NO_WARNINGS
#endif

#ifndef ONLINE_JUDGE
#define trace(...) __f(#__VA_ARGS__, __VA_ARGS__)
	template <typename Arg1>
	void __f(const char* name, Arg1&& arg1) { cerr << name << " : " << arg1 << endl; }
	template <typename Arg1, typename... Args>
	void __f(const char* names, Arg1&& arg1, Args&&... args) {
		const char* comma = strchr(names + 1, ',');
		cerr.write(names, comma - names) << " : " << arg1 << "  ";
		__f(comma + 1, args...);
	}
#else
#define trace(...)
#endif // ifndef ONLINE_JUDGE

struct custom_hash {
	static int splitmix64(int x) {
		x += 0x9e3779b97f4a7c15; x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
		return x ^ (x >> 31);
	}

	size_t operator()(int x) const {
		static const int FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
		return splitmix64(x + FIXED_RANDOM);
	}
};
template <class T, class cmp = less<T>>
using ordered_set = tree<T, null_type, cmp, rb_tree_tag, tree_order_statistics_node_update>;
#define unordered_map fast_unordered_map
template <class Key, class Value, class Hash = std::hash<Key>> // Hash = custom_hash for ints
using fast_unordered_map = gp_hash_table<Key, Value, Hash>;
#define unordered_set fast_unordered_set
template <class Key, class Hash = std::hash<Key>> // Hash = custom_hash for ints
using fast_unordered_set = gp_hash_table<Key, null_type, Hash>;

template <typename T> ostream& operator << (ostream& os, const vector<T>& v) { for (int i = 0; i < v.size(); ++i) os << v[i] << " "; return os; } 
template <typename T> ostream& operator << (ostream& os, const set<T>& v) { for (auto &it : v) os << it << " "; return os; } 
template <typename T, typename S> ostream& operator << (ostream& os, const pair<T, S>& v) { os << v.first << " " << v.second; return os; } 
template <typename T, typename S> ostream& operator << (ostream& os, const map<T, S>& v) { for (auto &it : v) os << "(" << it.first << "=>" << it.second << ")" << endl; return os; } 
template <typename T, typename S> ostream& operator << (ostream& os, const unordered_map<T, S>& v) { for (auto &it : v) os << "(" << it.first << "=>" << it.second << ")" << endl; return os; } 

const int mod = 1e9 + 7;
const int inf = 2e18;
const int ninf = -2e18;

int power(int x, int y, int mod = inf) 
{
	int ans = 1; 
	x %= mod;
	while(y > 0) 
	{
		if(y % 2) ans = (x * ans) % mod;
		x = (x * x) % mod;
		y >>= 1;
	}
	return ans;
}

const int N = 1e5 + 10;
bool isroot[N];
vector<int> graph[N];
int dp[N];

int dfs(int u)
{
	if(dp[u] != -1) return dp[u];
	dp[u] = 0;
	for(int v: graph[u]) {
		dp[u] = max(dp[u], 1 + dfs(v));
	}
	return dp[u];
}

signed main() 
{
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);

	time_t t1, t2;
	t1 = clock();

	int n, m;
	cin >> n >> m;

	reset(dp, -1);

	for(int i = 1; i <= m; i++)	
	{
		int x, y;
		cin >> x >> y;
		graph[x].emplace_back(y);
		isroot[y] = true;
	}

	int ans = 0;

	for(int i = 1; i <= n; i++)
	{
		if(!isroot[i]) 
			ans = max(ans, dfs(i));
	}

	cout << ans << endl;

	t2 = clock();
	#ifndef ONLINE_JUDGE
		cerr << endl << "time taken: " << t2 - t1 << endl;
	#endif // ONLINE_JUDGE

	return 0;
}

Measured memory_bytes: 9728