#include <bits/stdc++.h>
using namespace std;
using ll=long long int;
using ld=long double;
using VI=vector<ll>;
using VD=vector<ld>;
using VVI=vector<VI>;
using VC=vector<char>;
using VB=vector<bool>;
using VVC=vector<VC>;
using VS=vector<string>;
using PLL =pair<ll,ll>;
using PLD=pair<ld,ld>;
using VPLL=vector<PLL>;
#define print(x) std::cout<<x<<"\n"
#define rep(i,n) for(ll i=0;i<(ll)(n);i++)
#define repd(i,n) for(ll i=(ll)(n)-1;i>=0;i--)
#define ALL(x) (x).begin(),(x).end()
#define ALLR(x) (x).rbegin(),(x).rend()
#define SZ(x) ((ll)(x).size())
#define MAX(x) *max_element((x).begin(),(x).end())
#define MIN(x) *min_element((x).begin(),(x).end())
#define SORTR(x) sort((x).rbegin(),(x).rend())
#define SORT(x) sort((x).begin(),(x).end())
#define SUM(x) accumulate((x).begin(),(x).end(), 0)
#define FILL(x,a) fill(x.begin(),x.end(),a)
#define EACH(i,x) for(typeof((x).begin()) i=(x).begin(); i!=(x).end(); ++i)
#define EXIST(v, x) (std::find(v.begin(), v.end(), x) != v.end())

const ll INF = 1e18;
const ld EPS   = 1e-10;
const int MOD  = int(1e9)+7;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return 1; } return 0; }
template <class BidirectionalIterator>
bool next_partial_permutation(BidirectionalIterator first, BidirectionalIterator middle,BidirectionalIterator last){reverse(middle, last); return next_permutation(first , last);}
ll gcd(ll x, ll y) { return (x % y)? gcd(y, x % y): y; }
ll lcm(ll x, ll y) { return x / gcd(x, y) * y; }
ll GCD(VI v){ll a = v[0]; for (ll i = 1; i<SZ(v); i++) {a = gcd(a, v[i]);} return a;}
ll LCM(VI v){ll a = v[0]; for (ll i = 1; i<SZ(v); i++) {a = lcm(a, v[i]);} return a;}
VI Bit2Vector(const ll bit, ll n) {	VI s;	rep(i,n) if (bit & (1 << i)) s.push_back(i); return s;}

/*
struct Node{
	ll to;
	ll depth;
};

ll dfs(VVI &ab,ll s=0){
	deque<Node> que;// 次のNodeの値,今の通った数
	rep(i,SZ(ab)) for(ll j:ab[i]){
		if(ab[i][j]==0) que.push_front({i,0});
	}
	que.push_front({s,0});
	ll res=0;
	while(!que.empty()){// 空になるまで
		Node now=que.back();que.pop_front();//最後尾をとってくる
		for(ll id:ab[now.to]){
			Node next={id,now.depth+1};
			que.push_back(next);
			res=max(next.depth,res);
		}
	}
	return res;
}

void Main()
{
	ll n,m,l; ll res=0;
	string s,t,u; string sres="No or NO";

	cin>>n>>m;
	VVI ab(n);
	VI ress(n);
	rep(i, m) {
		ll a,b;
		cin >> a>>b;
		a--;b--;
		ab[a].emplace_back(b);
		//ab[b].emplace_back(a);
	}
	res=dfs(ab,0);
	print(res);
	return;
}
*/
// 入力
ll N, M;
VVI G; // グラフ
VI deg; // 各頂点の入次数

ll dp[100100];
void Main()
{

	// 入力受け取り, 入次数も管理する
	cin >> N >> M;
	G.assign(N, VI());
	deg.assign(N, 0);
	for (int i = 0; i<M; ++i) {
		int x, y;
		cin >> x >> y;
		--x, --y; // 0-indexed にする
		G[x].push_back(y);
		deg[y]++;
	}

	// source をキューに突っ込む
	queue<int> que;
	for (int v = 0; v<N; ++v) if (deg[v]==0) que.push(v);

	// BFS
	while (!que.empty()) {
		int v = que.front();
		que.pop();
		for (auto nv : G[v]) {
			deg[nv]--; // エッジ (v, nv) を破壊する
			if (deg[nv]==0) {
				que.push(nv); // それによって入次数が 0 にあったならキューに突っ込む
				chmax(dp[nv], dp[v]+1); // さらにソースから nv までの再長距離が確定する
			}
		}
	}

	// 答え
	ll res = 0;
	for (int v = 0; v<N; ++v) chmax(res, dp[v]);
	cout << res << endl;
}


int main()
{
	std::cin.tie(0);
	std::ios_base::sync_with_stdio(false);
	std::cout << std::fixed << std::setprecision(15);
	Main();
}


Measured memory_bytes: 7296